Multiview.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. #include "Multiview.hpp"
  2. #include <utility/display-helpers.hpp>
  3. #include <widgets/OBSBasic.hpp>
  4. #include <obs-frontend-api.h>
  5. Multiview::Multiview()
  6. {
  7. InitSafeAreas(&actionSafeMargin, &graphicsSafeMargin, &fourByThreeSafeMargin, &leftLine, &topLine, &rightLine);
  8. }
  9. Multiview::~Multiview()
  10. {
  11. for (OBSWeakSource &weakSrc : multiviewScenes) {
  12. OBSSource src = OBSGetStrongRef(weakSrc);
  13. if (src)
  14. obs_source_dec_showing(src);
  15. }
  16. obs_enter_graphics();
  17. gs_vertexbuffer_destroy(actionSafeMargin);
  18. gs_vertexbuffer_destroy(graphicsSafeMargin);
  19. gs_vertexbuffer_destroy(fourByThreeSafeMargin);
  20. gs_vertexbuffer_destroy(leftLine);
  21. gs_vertexbuffer_destroy(topLine);
  22. gs_vertexbuffer_destroy(rightLine);
  23. obs_leave_graphics();
  24. }
  25. static OBSSource CreateLabel(const char *name, size_t h)
  26. {
  27. OBSDataAutoRelease settings = obs_data_create();
  28. OBSDataAutoRelease font = obs_data_create();
  29. std::string text;
  30. text += " ";
  31. text += name;
  32. text += " ";
  33. #if defined(_WIN32)
  34. obs_data_set_string(font, "face", "Arial");
  35. #elif defined(__APPLE__)
  36. obs_data_set_string(font, "face", "Helvetica");
  37. #else
  38. obs_data_set_string(font, "face", "Monospace");
  39. #endif
  40. obs_data_set_int(font, "flags", 1); // Bold text
  41. obs_data_set_int(font, "size", int(h / 9.81));
  42. obs_data_set_obj(settings, "font", font);
  43. obs_data_set_string(settings, "text", text.c_str());
  44. obs_data_set_bool(settings, "outline", false);
  45. obs_data_set_int(settings, "opacity", 50);
  46. #ifdef _WIN32
  47. const char *text_source_id = "text_gdiplus";
  48. #else
  49. const char *text_source_id = "text_ft2_source";
  50. #endif
  51. OBSSourceAutoRelease txtSource = obs_source_create_private(text_source_id, name, settings);
  52. return txtSource.Get();
  53. }
  54. void Multiview::Update(MultiviewLayout multiviewLayout, bool drawLabel, bool drawSafeArea)
  55. {
  56. this->multiviewLayout = multiviewLayout;
  57. this->drawLabel = drawLabel;
  58. this->drawSafeArea = drawSafeArea;
  59. struct obs_video_info ovi;
  60. obs_get_video_info(&ovi);
  61. uint32_t w = ovi.base_width;
  62. uint32_t h = ovi.base_height;
  63. fw = float(w);
  64. fh = float(h);
  65. ratio = fw / fh;
  66. struct obs_frontend_source_list scenes = {};
  67. obs_frontend_get_scenes(&scenes);
  68. switch (multiviewLayout) {
  69. case MultiviewLayout::HORIZONTAL_TOP_18_SCENES:
  70. pvwprgCX = fw / 2;
  71. pvwprgCY = fh / 2;
  72. maxSrcs = 18;
  73. break;
  74. case MultiviewLayout::HORIZONTAL_TOP_24_SCENES:
  75. pvwprgCX = fw / 3;
  76. pvwprgCY = fh / 3;
  77. maxSrcs = 24;
  78. break;
  79. case MultiviewLayout::SCENES_ONLY_4_SCENES:
  80. pvwprgCX = fw / 2;
  81. pvwprgCY = fh / 2;
  82. maxSrcs = 4;
  83. break;
  84. case MultiviewLayout::SCENES_ONLY_9_SCENES:
  85. pvwprgCX = fw / 3;
  86. pvwprgCY = fh / 3;
  87. maxSrcs = 9;
  88. break;
  89. case MultiviewLayout::SCENES_ONLY_16_SCENES:
  90. pvwprgCX = fw / 4;
  91. pvwprgCY = fh / 4;
  92. maxSrcs = 16;
  93. break;
  94. case MultiviewLayout::SCENES_ONLY_25_SCENES:
  95. pvwprgCX = fw / 5;
  96. pvwprgCY = fh / 5;
  97. maxSrcs = 25;
  98. break;
  99. default:
  100. pvwprgCX = fw / 2;
  101. pvwprgCY = fh / 2;
  102. maxSrcs = 8;
  103. }
  104. ppiCX = pvwprgCX - thicknessx2;
  105. ppiCY = pvwprgCY - thicknessx2;
  106. ppiScaleX = (pvwprgCX - thicknessx2) / fw;
  107. ppiScaleY = (pvwprgCY - thicknessx2) / fh;
  108. switch (multiviewLayout) {
  109. case MultiviewLayout::HORIZONTAL_TOP_18_SCENES:
  110. scenesCX = pvwprgCX / 3;
  111. scenesCY = pvwprgCY / 3;
  112. break;
  113. case MultiviewLayout::SCENES_ONLY_4_SCENES:
  114. case MultiviewLayout::SCENES_ONLY_9_SCENES:
  115. case MultiviewLayout::SCENES_ONLY_16_SCENES:
  116. case MultiviewLayout::SCENES_ONLY_25_SCENES:
  117. scenesCX = pvwprgCX;
  118. scenesCY = pvwprgCY;
  119. break;
  120. default:
  121. scenesCX = pvwprgCX / 2;
  122. scenesCY = pvwprgCY / 2;
  123. }
  124. siCX = scenesCX - thicknessx2;
  125. siCY = scenesCY - thicknessx2;
  126. siScaleX = (scenesCX - thicknessx2) / fw;
  127. siScaleY = (scenesCY - thicknessx2) / fh;
  128. std::vector<OBSWeakSource> updatedScenes;
  129. std::vector<OBSSource> updatedLabels;
  130. updatedLabels.emplace_back(CreateLabel(Str("StudioMode.Preview"), h / 2));
  131. updatedLabels.emplace_back(CreateLabel(Str("StudioMode.Program"), h / 2));
  132. size_t i = 0;
  133. while (i < scenes.sources.num && updatedScenes.size() < maxSrcs) {
  134. obs_source_t *src = scenes.sources.array[i++];
  135. OBSDataAutoRelease data = obs_source_get_private_settings(src);
  136. obs_data_set_default_bool(data, "show_in_multiview", true);
  137. if (!obs_data_get_bool(data, "show_in_multiview"))
  138. continue;
  139. updatedScenes.emplace_back(OBSGetWeakRef(src));
  140. obs_source_inc_showing(src);
  141. updatedLabels.emplace_back(CreateLabel(obs_source_get_name(src), h / 3));
  142. }
  143. obs_frontend_source_list_free(&scenes);
  144. for (OBSWeakSource &weakSrc : multiviewScenes) {
  145. OBSSource src = OBSGetStrongRef(weakSrc);
  146. if (src)
  147. obs_source_dec_showing(src);
  148. }
  149. multiviewScenes = std::move(updatedScenes);
  150. multiviewLabels = std::move(updatedLabels);
  151. }
  152. static inline uint32_t labelOffset(MultiviewLayout multiviewLayout, obs_source_t *label, uint32_t cx)
  153. {
  154. uint32_t w = obs_source_get_width(label);
  155. int n; // Twice of scale factor of preview and program scenes
  156. switch (multiviewLayout) {
  157. case MultiviewLayout::HORIZONTAL_TOP_24_SCENES:
  158. n = 6;
  159. break;
  160. case MultiviewLayout::SCENES_ONLY_25_SCENES:
  161. n = 10;
  162. break;
  163. case MultiviewLayout::SCENES_ONLY_16_SCENES:
  164. n = 8;
  165. break;
  166. case MultiviewLayout::SCENES_ONLY_9_SCENES:
  167. n = 6;
  168. break;
  169. case MultiviewLayout::SCENES_ONLY_4_SCENES:
  170. n = 4;
  171. break;
  172. default:
  173. n = 4;
  174. break;
  175. }
  176. w = uint32_t(w * ((1.0f) / n));
  177. return (cx / 2) - w;
  178. }
  179. void Multiview::Render(uint32_t cx, uint32_t cy)
  180. {
  181. OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window();
  182. uint32_t targetCX, targetCY;
  183. int x, y;
  184. float scale;
  185. targetCX = (uint32_t)fw;
  186. targetCY = (uint32_t)fh;
  187. GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale);
  188. OBSSource previewSrc = main->GetCurrentSceneSource();
  189. OBSSource programSrc = main->GetProgramSource();
  190. bool studioMode = main->IsPreviewProgramMode();
  191. auto drawBox = [&](float cx, float cy, uint32_t colorVal) {
  192. gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
  193. gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color");
  194. gs_effect_set_color(color, colorVal);
  195. while (gs_effect_loop(solid, "Solid"))
  196. gs_draw_sprite(nullptr, 0, (uint32_t)cx, (uint32_t)cy);
  197. };
  198. auto setRegion = [&](float bx, float by, float cx, float cy) {
  199. float vX = int(x + bx * scale);
  200. float vY = int(y + by * scale);
  201. float vCX = int(cx * scale);
  202. float vCY = int(cy * scale);
  203. float oL = bx;
  204. float oT = by;
  205. float oR = (bx + cx);
  206. float oB = (by + cy);
  207. startRegion(vX, vY, vCX, vCY, oL, oR, oT, oB);
  208. };
  209. auto calcBaseSource = [&](size_t i) {
  210. switch (multiviewLayout) {
  211. case MultiviewLayout::HORIZONTAL_TOP_18_SCENES:
  212. sourceX = (i % 6) * scenesCX;
  213. sourceY = pvwprgCY + (i / 6) * scenesCY;
  214. break;
  215. case MultiviewLayout::HORIZONTAL_TOP_24_SCENES:
  216. sourceX = (i % 6) * scenesCX;
  217. sourceY = pvwprgCY + (i / 6) * scenesCY;
  218. break;
  219. case MultiviewLayout::VERTICAL_LEFT_8_SCENES:
  220. sourceX = pvwprgCX;
  221. sourceY = (i / 2) * scenesCY;
  222. if (i % 2 != 0)
  223. sourceX += scenesCX;
  224. break;
  225. case MultiviewLayout::VERTICAL_RIGHT_8_SCENES:
  226. sourceX = 0;
  227. sourceY = (i / 2) * scenesCY;
  228. if (i % 2 != 0)
  229. sourceX = scenesCX;
  230. break;
  231. case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES:
  232. if (i < 4) {
  233. sourceX = (float(i) * scenesCX);
  234. sourceY = 0;
  235. } else {
  236. sourceX = (float(i - 4) * scenesCX);
  237. sourceY = scenesCY;
  238. }
  239. break;
  240. case MultiviewLayout::SCENES_ONLY_4_SCENES:
  241. sourceX = (i % 2) * scenesCX;
  242. sourceY = (i / 2) * scenesCY;
  243. break;
  244. case MultiviewLayout::SCENES_ONLY_9_SCENES:
  245. sourceX = (i % 3) * scenesCX;
  246. sourceY = (i / 3) * scenesCY;
  247. break;
  248. case MultiviewLayout::SCENES_ONLY_16_SCENES:
  249. sourceX = (i % 4) * scenesCX;
  250. sourceY = (i / 4) * scenesCY;
  251. break;
  252. case MultiviewLayout::SCENES_ONLY_25_SCENES:
  253. sourceX = (i % 5) * scenesCX;
  254. sourceY = (i / 5) * scenesCY;
  255. break;
  256. default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES:
  257. if (i < 4) {
  258. sourceX = (float(i) * scenesCX);
  259. sourceY = pvwprgCY;
  260. } else {
  261. sourceX = (float(i - 4) * scenesCX);
  262. sourceY = pvwprgCY + scenesCY;
  263. }
  264. }
  265. siX = sourceX + thickness;
  266. siY = sourceY + thickness;
  267. };
  268. auto calcPreviewProgram = [&](bool program) {
  269. switch (multiviewLayout) {
  270. case MultiviewLayout::HORIZONTAL_TOP_24_SCENES:
  271. sourceX = thickness + pvwprgCX / 2;
  272. sourceY = thickness;
  273. labelX = offset + pvwprgCX / 2;
  274. labelY = pvwprgCY;
  275. if (program) {
  276. sourceX += pvwprgCX;
  277. labelX += pvwprgCX;
  278. }
  279. break;
  280. case MultiviewLayout::VERTICAL_LEFT_8_SCENES:
  281. sourceX = thickness;
  282. sourceY = pvwprgCY + thickness;
  283. labelX = offset;
  284. labelY = pvwprgCY * 2;
  285. if (program) {
  286. sourceY = thickness;
  287. labelY = pvwprgCY;
  288. }
  289. break;
  290. case MultiviewLayout::VERTICAL_RIGHT_8_SCENES:
  291. sourceX = pvwprgCX + thickness;
  292. sourceY = pvwprgCY + thickness;
  293. labelX = pvwprgCX + offset;
  294. labelY = pvwprgCY * 2;
  295. if (program) {
  296. sourceY = thickness;
  297. labelY = pvwprgCY;
  298. }
  299. break;
  300. case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES:
  301. sourceX = thickness;
  302. sourceY = pvwprgCY + thickness;
  303. labelX = offset;
  304. labelY = pvwprgCY * 2;
  305. if (program) {
  306. sourceX += pvwprgCX;
  307. labelX += pvwprgCX;
  308. }
  309. break;
  310. case MultiviewLayout::SCENES_ONLY_4_SCENES:
  311. case MultiviewLayout::SCENES_ONLY_9_SCENES:
  312. case MultiviewLayout::SCENES_ONLY_16_SCENES:
  313. sourceX = thickness;
  314. sourceY = thickness;
  315. labelX = offset;
  316. break;
  317. default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES and 18_SCENES
  318. sourceX = thickness;
  319. sourceY = thickness;
  320. labelX = offset;
  321. labelY = pvwprgCY;
  322. if (program) {
  323. sourceX += pvwprgCX;
  324. labelX += pvwprgCX;
  325. }
  326. }
  327. };
  328. auto paintAreaWithColor = [&](float tx, float ty, float cx, float cy, uint32_t color) {
  329. gs_matrix_push();
  330. gs_matrix_translate3f(tx, ty, 0.0f);
  331. drawBox(cx, cy, color);
  332. gs_matrix_pop();
  333. };
  334. // Define the whole usable region for the multiview
  335. startRegion(x, y, targetCX * scale, targetCY * scale, 0.0f, fw, 0.0f, fh);
  336. // Change the background color to highlight all sources
  337. drawBox(fw, fh, outerColor);
  338. /* ----------------------------- */
  339. /* draw sources */
  340. for (size_t i = 0; i < maxSrcs; i++) {
  341. // Handle all the offsets
  342. calcBaseSource(i);
  343. if (i >= multiviewScenes.size()) {
  344. // Just paint the background and continue
  345. paintAreaWithColor(sourceX, sourceY, scenesCX, scenesCY, outerColor);
  346. paintAreaWithColor(siX, siY, siCX, siCY, backgroundColor);
  347. continue;
  348. }
  349. OBSSource src = OBSGetStrongRef(multiviewScenes[i]);
  350. // We have a source. Now chose the proper highlight color
  351. uint32_t colorVal = outerColor;
  352. if (src == programSrc)
  353. colorVal = programColor;
  354. else if (src == previewSrc)
  355. colorVal = studioMode ? previewColor : programColor;
  356. // Paint the background
  357. paintAreaWithColor(sourceX, sourceY, scenesCX, scenesCY, colorVal);
  358. paintAreaWithColor(siX, siY, siCX, siCY, backgroundColor);
  359. /* ----------- */
  360. // Render the source
  361. gs_matrix_push();
  362. gs_matrix_translate3f(siX, siY, 0.0f);
  363. gs_matrix_scale3f(siScaleX, siScaleY, 1.0f);
  364. setRegion(siX, siY, siCX, siCY);
  365. obs_source_video_render(src);
  366. endRegion();
  367. gs_matrix_pop();
  368. /* ----------- */
  369. // Render the label
  370. if (!drawLabel)
  371. continue;
  372. obs_source *label = multiviewLabels[i + 2];
  373. if (!label)
  374. continue;
  375. offset = labelOffset(multiviewLayout, label, scenesCX);
  376. gs_matrix_push();
  377. gs_matrix_translate3f(sourceX + offset,
  378. sourceY + scenesCY - (obs_source_get_height(label) * ppiScaleY) - (thickness * 3),
  379. 0.0f);
  380. gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);
  381. drawBox(obs_source_get_width(label), obs_source_get_height(label) + thicknessx2, labelColor);
  382. gs_matrix_translate3f(0, thickness, 0.0f);
  383. obs_source_video_render(label);
  384. gs_matrix_pop();
  385. }
  386. if (multiviewLayout == MultiviewLayout::SCENES_ONLY_4_SCENES ||
  387. multiviewLayout == MultiviewLayout::SCENES_ONLY_9_SCENES ||
  388. multiviewLayout == MultiviewLayout::SCENES_ONLY_16_SCENES ||
  389. multiviewLayout == MultiviewLayout::SCENES_ONLY_25_SCENES) {
  390. endRegion();
  391. return;
  392. }
  393. /* ----------------------------- */
  394. /* draw preview */
  395. obs_source_t *previewLabel = multiviewLabels[0];
  396. offset = labelOffset(multiviewLayout, previewLabel, pvwprgCX);
  397. calcPreviewProgram(false);
  398. // Paint the background
  399. paintAreaWithColor(sourceX, sourceY, ppiCX, ppiCY, backgroundColor);
  400. // Scale and Draw the preview
  401. gs_matrix_push();
  402. gs_matrix_translate3f(sourceX, sourceY, 0.0f);
  403. gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);
  404. setRegion(sourceX, sourceY, ppiCX, ppiCY);
  405. if (studioMode)
  406. obs_source_video_render(previewSrc);
  407. else
  408. obs_render_main_texture();
  409. if (drawSafeArea) {
  410. RenderSafeAreas(actionSafeMargin, targetCX, targetCY);
  411. RenderSafeAreas(graphicsSafeMargin, targetCX, targetCY);
  412. RenderSafeAreas(fourByThreeSafeMargin, targetCX, targetCY);
  413. RenderSafeAreas(leftLine, targetCX, targetCY);
  414. RenderSafeAreas(topLine, targetCX, targetCY);
  415. RenderSafeAreas(rightLine, targetCX, targetCY);
  416. }
  417. endRegion();
  418. gs_matrix_pop();
  419. /* ----------- */
  420. // Draw the Label
  421. if (drawLabel) {
  422. gs_matrix_push();
  423. gs_matrix_translate3f(
  424. labelX, labelY - (obs_source_get_height(previewLabel) * ppiScaleY) - (thickness * 3), 0.0f);
  425. gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);
  426. drawBox(obs_source_get_width(previewLabel), obs_source_get_height(previewLabel) + thicknessx2,
  427. labelColor);
  428. gs_matrix_translate3f(0, thickness, 0.0f);
  429. obs_source_video_render(previewLabel);
  430. gs_matrix_pop();
  431. }
  432. /* ----------------------------- */
  433. /* draw program */
  434. obs_source_t *programLabel = multiviewLabels[1];
  435. offset = labelOffset(multiviewLayout, programLabel, pvwprgCX);
  436. calcPreviewProgram(true);
  437. paintAreaWithColor(sourceX, sourceY, ppiCX, ppiCY, backgroundColor);
  438. // Scale and Draw the program
  439. gs_matrix_push();
  440. gs_matrix_translate3f(sourceX, sourceY, 0.0f);
  441. gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);
  442. setRegion(sourceX, sourceY, ppiCX, ppiCY);
  443. obs_render_main_texture();
  444. endRegion();
  445. gs_matrix_pop();
  446. /* ----------- */
  447. // Draw the Label
  448. if (drawLabel) {
  449. gs_matrix_push();
  450. gs_matrix_translate3f(
  451. labelX, labelY - (obs_source_get_height(programLabel) * ppiScaleY) - (thickness * 3), 0.0f);
  452. gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);
  453. drawBox(obs_source_get_width(programLabel), obs_source_get_height(programLabel) + thicknessx2,
  454. labelColor);
  455. gs_matrix_translate3f(0, thickness, 0.0f);
  456. obs_source_video_render(programLabel);
  457. gs_matrix_pop();
  458. }
  459. // Region for future usage with additional info.
  460. if (multiviewLayout == MultiviewLayout::HORIZONTAL_TOP_24_SCENES) {
  461. // Just paint the background for now
  462. paintAreaWithColor(thickness, thickness, siCX, siCY * 2 + thicknessx2, backgroundColor);
  463. paintAreaWithColor(thickness + 2.5 * (thicknessx2 + ppiCX), thickness, siCX, siCY * 2 + thicknessx2,
  464. backgroundColor);
  465. }
  466. endRegion();
  467. }
  468. OBSSource Multiview::GetSourceByPosition(int x, int y)
  469. {
  470. int pos = -1;
  471. QWidget *rec = QApplication::activeWindow();
  472. if (!rec)
  473. return nullptr;
  474. int cx = rec->width();
  475. int cy = rec->height();
  476. int minX = 0;
  477. int minY = 0;
  478. int maxX = cx;
  479. int maxY = cy;
  480. switch (multiviewLayout) {
  481. case MultiviewLayout::HORIZONTAL_TOP_18_SCENES:
  482. if (float(cx) / float(cy) > ratio) {
  483. int validX = cy * ratio;
  484. minX = (cx / 2) - (validX / 2);
  485. maxX = (cx / 2) + (validX / 2);
  486. } else {
  487. int validY = cx / ratio;
  488. maxY = (cy / 2) + (validY / 2);
  489. }
  490. minY = cy / 2;
  491. if (x < minX || x > maxX || y < minY || y > maxY)
  492. break;
  493. pos = (x - minX) / ((maxX - minX) / 6);
  494. pos += ((y - minY) / ((maxY - minY) / 3)) * 6;
  495. break;
  496. case MultiviewLayout::HORIZONTAL_TOP_24_SCENES:
  497. if (float(cx) / float(cy) > ratio) {
  498. int validX = cy * ratio;
  499. minX = (cx / 2) - (validX / 2);
  500. maxX = (cx / 2) + (validX / 2);
  501. minY = cy / 3;
  502. } else {
  503. int validY = cx / ratio;
  504. maxY = (cy / 2) + (validY / 2);
  505. minY = (cy / 2) - (validY / 6);
  506. }
  507. if (x < minX || x > maxX || y < minY || y > maxY)
  508. break;
  509. pos = (x - minX) / ((maxX - minX) / 6);
  510. pos += ((y - minY) / ((maxY - minY) / 4)) * 6;
  511. break;
  512. case MultiviewLayout::VERTICAL_LEFT_8_SCENES:
  513. if (float(cx) / float(cy) > ratio) {
  514. int validX = cy * ratio;
  515. maxX = (cx / 2) + (validX / 2);
  516. } else {
  517. int validY = cx / ratio;
  518. minY = (cy / 2) - (validY / 2);
  519. maxY = (cy / 2) + (validY / 2);
  520. }
  521. minX = cx / 2;
  522. if (x < minX || x > maxX || y < minY || y > maxY)
  523. break;
  524. pos = 2 * ((y - minY) / ((maxY - minY) / 4));
  525. if (x > minX + ((maxX - minX) / 2))
  526. pos++;
  527. break;
  528. case MultiviewLayout::VERTICAL_RIGHT_8_SCENES:
  529. if (float(cx) / float(cy) > ratio) {
  530. int validX = cy * ratio;
  531. minX = (cx / 2) - (validX / 2);
  532. } else {
  533. int validY = cx / ratio;
  534. minY = (cy / 2) - (validY / 2);
  535. maxY = (cy / 2) + (validY / 2);
  536. }
  537. maxX = (cx / 2);
  538. if (x < minX || x > maxX || y < minY || y > maxY)
  539. break;
  540. pos = 2 * ((y - minY) / ((maxY - minY) / 4));
  541. if (x > minX + ((maxX - minX) / 2))
  542. pos++;
  543. break;
  544. case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES:
  545. if (float(cx) / float(cy) > ratio) {
  546. int validX = cy * ratio;
  547. minX = (cx / 2) - (validX / 2);
  548. maxX = (cx / 2) + (validX / 2);
  549. } else {
  550. int validY = cx / ratio;
  551. minY = (cy / 2) - (validY / 2);
  552. }
  553. maxY = (cy / 2);
  554. if (x < minX || x > maxX || y < minY || y > maxY)
  555. break;
  556. pos = (x - minX) / ((maxX - minX) / 4);
  557. if (y > minY + ((maxY - minY) / 2))
  558. pos += 4;
  559. break;
  560. case MultiviewLayout::SCENES_ONLY_4_SCENES:
  561. if (float(cx) / float(cy) > ratio) {
  562. int validX = cy * ratio;
  563. minX = (cx / 2) - (validX / 2);
  564. maxX = (cx / 2) + (validX / 2);
  565. } else {
  566. int validY = cx / ratio;
  567. maxY = (cy / 2) + (validY / 2);
  568. minY = (cy / 2) - (validY / 2);
  569. }
  570. if (x < minX || x > maxX || y < minY || y > maxY)
  571. break;
  572. pos = (x - minX) / ((maxX - minX) / 2);
  573. pos += ((y - minY) / ((maxY - minY) / 2)) * 2;
  574. break;
  575. case MultiviewLayout::SCENES_ONLY_9_SCENES:
  576. if (float(cx) / float(cy) > ratio) {
  577. int validX = cy * ratio;
  578. minX = (cx / 2) - (validX / 2);
  579. maxX = (cx / 2) + (validX / 2);
  580. } else {
  581. int validY = cx / ratio;
  582. maxY = (cy / 2) + (validY / 2);
  583. minY = (cy / 2) - (validY / 2);
  584. }
  585. if (x < minX || x > maxX || y < minY || y > maxY)
  586. break;
  587. pos = (x - minX) / ((maxX - minX) / 3);
  588. pos += ((y - minY) / ((maxY - minY) / 3)) * 3;
  589. break;
  590. case MultiviewLayout::SCENES_ONLY_16_SCENES:
  591. if (float(cx) / float(cy) > ratio) {
  592. int validX = cy * ratio;
  593. minX = (cx / 2) - (validX / 2);
  594. maxX = (cx / 2) + (validX / 2);
  595. } else {
  596. int validY = cx / ratio;
  597. maxY = (cy / 2) + (validY / 2);
  598. minY = (cy / 2) - (validY / 2);
  599. }
  600. if (x < minX || x > maxX || y < minY || y > maxY)
  601. break;
  602. pos = (x - minX) / ((maxX - minX) / 4);
  603. pos += ((y - minY) / ((maxY - minY) / 4)) * 4;
  604. break;
  605. case MultiviewLayout::SCENES_ONLY_25_SCENES:
  606. if (float(cx) / float(cy) > ratio) {
  607. int validX = cy * ratio;
  608. minX = (cx / 2) - (validX / 2);
  609. maxX = (cx / 2) + (validX / 2);
  610. } else {
  611. int validY = cx / ratio;
  612. maxY = (cy / 2) + (validY / 2);
  613. minY = (cy / 2) - (validY / 2);
  614. }
  615. if (x < minX || x > maxX || y < minY || y > maxY)
  616. break;
  617. pos = (x - minX) / ((maxX - minX) / 5);
  618. pos += ((y - minY) / ((maxY - minY) / 5)) * 5;
  619. break;
  620. default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES
  621. if (float(cx) / float(cy) > ratio) {
  622. int validX = cy * ratio;
  623. minX = (cx / 2) - (validX / 2);
  624. maxX = (cx / 2) + (validX / 2);
  625. } else {
  626. int validY = cx / ratio;
  627. maxY = (cy / 2) + (validY / 2);
  628. }
  629. minY = (cy / 2);
  630. if (x < minX || x > maxX || y < minY || y > maxY)
  631. break;
  632. pos = (x - minX) / ((maxX - minX) / 4);
  633. if (y > minY + ((maxY - minY) / 2))
  634. pos += 4;
  635. }
  636. if (pos < 0 || pos >= (int)multiviewScenes.size())
  637. return nullptr;
  638. return OBSGetStrongRef(multiviewScenes[pos]);
  639. }