Multiview.cpp 19 KB

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