calls_group_viewport_opengl.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "calls/group/calls_group_viewport_opengl.h"
  8. #include "calls/group/calls_group_viewport_tile.h"
  9. #include "webrtc/webrtc_video_track.h"
  10. #include "media/view/media_view_pip.h"
  11. #include "media/streaming/media_streaming_utility.h"
  12. #include "calls/group/calls_group_members_row.h"
  13. #include "lang/lang_keys.h"
  14. #include "ui/gl/gl_shader.h"
  15. #include "ui/painter.h"
  16. #include "data/data_peer.h"
  17. #include "styles/style_calls.h"
  18. #include <QOpenGLShader>
  19. namespace Calls::Group {
  20. namespace {
  21. using namespace Ui::GL;
  22. constexpr auto kScaleForBlurTextureIndex = 3;
  23. constexpr auto kFirstBlurPassTextureIndex = 4;
  24. constexpr auto kNoiseTextureSize = 256;
  25. // The more the scale - more blurred the image.
  26. constexpr auto kBlurTextureSizeFactor = 4.;
  27. constexpr auto kBlurOpacity = 0.65;
  28. constexpr auto kDitherNoiseAmount = 0.002;
  29. constexpr auto kQuads = 9;
  30. constexpr auto kQuadVertices = kQuads * 4;
  31. constexpr auto kQuadValues = kQuadVertices * 4;
  32. constexpr auto kValues = kQuadValues + 8; // Blur texture coordinates.
  33. [[nodiscard]] ShaderPart FragmentBlurTexture(
  34. bool vertical,
  35. char prefix = 'v') {
  36. const auto offsets = (vertical ? QString("0, 1") : QString("1, 0"));
  37. const auto name = prefix + QString("_texcoord");
  38. return {
  39. .header = R"(
  40. varying vec2 )" + name + R"(;
  41. uniform sampler2D b_texture;
  42. uniform float texelOffset;
  43. const vec3 satLuminanceWeighting = vec3(0.2126, 0.7152, 0.0722);
  44. const vec2 offsets = vec2()" + offsets + R"();
  45. const int radius = 15;
  46. const int diameter = 2 * radius + 1;
  47. )",
  48. .body = R"(
  49. vec4 accumulated = vec4(0.);
  50. for (int i = 0; i != diameter; i++) {
  51. float stepOffset = float(i - radius) * texelOffset;
  52. vec2 offset = vec2(stepOffset) * offsets;
  53. vec4 sampled = vec4(texture2D(b_texture, )" + name + R"( + offset));
  54. float fradius = float(radius);
  55. float boxWeight = fradius + 1.0 - abs(float(i) - fradius);
  56. accumulated += sampled * boxWeight;
  57. }
  58. vec3 blurred = accumulated.rgb / accumulated.a;
  59. float satLuminance = dot(blurred, satLuminanceWeighting);
  60. vec3 mixinColor = vec3(satLuminance);
  61. result = vec4(clamp(mix(mixinColor, blurred, 1.1), 0.0, 1.0), 1.0);
  62. )",
  63. };
  64. }
  65. [[nodiscard]] ShaderPart FragmentGenerateNoise() {
  66. const auto size = QString::number(kNoiseTextureSize);
  67. return {
  68. .header = R"(
  69. const float permTexUnit = 1.0 / )" + size + R"(.0;
  70. const float permTexUnitHalf = 0.5 / )" + size + R"(.0;
  71. const float grainsize = 1.3;
  72. const float noiseCoordRotation = 1.425;
  73. const vec2 dimensions = vec2()" + size + ", " + size + R"();
  74. vec4 rnm(vec2 tc) {
  75. float noise = sin(dot(tc, vec2(12.9898, 78.233))) * 43758.5453;
  76. return vec4(
  77. fract(noise),
  78. fract(noise * 1.2154),
  79. fract(noise * 1.3453),
  80. fract(noise * 1.3647)
  81. ) * 2.0 - 1.0;
  82. }
  83. float fade(float t) {
  84. return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
  85. }
  86. float pnoise3D(vec3 p) {
  87. vec3 pi = permTexUnit * floor(p) + permTexUnitHalf;
  88. vec3 pf = fract(p);
  89. float perm = rnm(pi.xy).a;
  90. float n000 = dot(rnm(vec2(perm, pi.z)).rgb * 4.0 - 1.0, pf);
  91. float n001 = dot(
  92. rnm(vec2(perm, pi.z + permTexUnit)).rgb * 4.0 - 1.0,
  93. pf - vec3(0.0, 0.0, 1.0));
  94. perm = rnm(pi.xy + vec2(0.0, permTexUnit)).a;
  95. float n010 = dot(
  96. rnm(vec2(perm, pi.z)).rgb * 4.0 - 1.0,
  97. pf - vec3(0.0, 1.0, 0.0));
  98. float n011 = dot(
  99. rnm(vec2(perm, pi.z + permTexUnit)).rgb * 4.0 - 1.0,
  100. pf - vec3(0.0, 1.0, 1.0));
  101. perm = rnm(pi.xy + vec2(permTexUnit, 0.0)).a;
  102. float n100 = dot(
  103. rnm(vec2(perm, pi.z)).rgb * 4.0 - 1.0,
  104. pf - vec3(1.0, 0.0, 0.0));
  105. float n101 = dot(
  106. rnm(vec2(perm, pi.z + permTexUnit)).rgb * 4.0 - 1.0,
  107. pf - vec3(1.0, 0.0, 1.0));
  108. perm = rnm(pi.xy + vec2(permTexUnit, permTexUnit)).a;
  109. float n110 = dot(
  110. rnm(vec2(perm, pi.z)).rgb * 4.0 - 1.0,
  111. pf - vec3(1.0, 1.0, 0.0));
  112. float n111 = dot(
  113. rnm(vec2(perm, pi.z + permTexUnit)).rgb * 4.0 - 1.0,
  114. pf - vec3(1.0, 1.0, 1.0));
  115. vec4 n_x = mix(
  116. vec4(n000, n001, n010, n011),
  117. vec4(n100, n101, n110, n111),
  118. fade(pf.x));
  119. vec2 n_xy = mix(n_x.xy, n_x.zw, fade(pf.y));
  120. return mix(n_xy.x, n_xy.y, fade(pf.z));
  121. }
  122. vec2 rotateTexCoords(in lowp vec2 tc, in lowp float angle) {
  123. float cosa = cos(angle);
  124. float sina = sin(angle);
  125. return vec2(
  126. ((tc.x * 2.0 - 1.0) * cosa - (tc.y * 2.0 - 1.0) * sina) * 0.5 + 0.5,
  127. ((tc.y * 2.0 - 1.0) * cosa + (tc.x * 2.0 - 1.0) * sina) * 0.5 + 0.5);
  128. }
  129. )",
  130. .body = R"(
  131. vec2 rotatedCoords = rotateTexCoords(
  132. gl_FragCoord.xy / dimensions.xy,
  133. noiseCoordRotation);
  134. float intensity = pnoise3D(vec3(
  135. rotatedCoords.x * dimensions.x / grainsize,
  136. rotatedCoords.y * dimensions.y / grainsize,
  137. 0.0));
  138. // Looks like intensity is almost always in [-2, 2] range.
  139. float clamped = clamp((intensity + 2.) * 0.25, 0., 1.);
  140. result = vec4(clamped, 0., 0., 1.);
  141. )",
  142. };
  143. }
  144. [[nodiscard]] ShaderPart FragmentDitherNoise() {
  145. const auto size = QString::number(kNoiseTextureSize);
  146. return {
  147. .header = R"(
  148. uniform sampler2D n_texture;
  149. )",
  150. .body = R"(
  151. vec2 noiseTextureCoord = gl_FragCoord.xy / )" + size + R"(.;
  152. float noiseClamped = texture2D(n_texture, noiseTextureCoord).r;
  153. float noiseIntensity = (noiseClamped * 4.) - 2.;
  154. vec3 lumcoeff = vec3(0.299, 0.587, 0.114);
  155. float luminance = dot(result.rgb, lumcoeff);
  156. float lum = smoothstep(0.2, 0.0, luminance) + luminance;
  157. vec3 noiseColor = mix(vec3(noiseIntensity), vec3(0.0), pow(lum, 4.0));
  158. result.rgb = result.rgb + noiseColor * noiseGrain;
  159. )",
  160. };
  161. }
  162. // Depends on FragmentSampleTexture().
  163. [[nodiscard]] ShaderPart FragmentFrameColor() {
  164. const auto round = FragmentRoundCorners();
  165. const auto blur = FragmentBlurTexture(true, 'b');
  166. const auto noise = FragmentDitherNoise();
  167. return {
  168. .header = R"(
  169. uniform vec4 frameBg;
  170. uniform vec4 shadow; // fullHeight, shown, maxOpacity, blur opacity
  171. uniform float paused; // 0. <-> 1.
  172. )" + blur.header + round.header + noise.header + R"(
  173. const float noiseGrain = )" + QString::number(kDitherNoiseAmount) + R"(;
  174. float insideTexture() {
  175. vec2 textureHalf = vec2(0.5, 0.5);
  176. vec2 fromTextureCenter = abs(v_texcoord - textureHalf);
  177. vec2 fromTextureEdge = max(fromTextureCenter, textureHalf) - textureHalf;
  178. float outsideCheck = dot(fromTextureEdge, fromTextureEdge);
  179. return step(outsideCheck, 0.);
  180. }
  181. vec4 background() {
  182. vec4 result;
  183. )" + blur.body + noise.body + R"(
  184. return result;
  185. }
  186. )",
  187. .body = R"(
  188. float inside = insideTexture() * (1. - paused);
  189. float backgroundOpacity = shadow.w;
  190. result = result * inside
  191. + (1. - inside) * (backgroundOpacity * background()
  192. + (1. - backgroundOpacity) * frameBg);
  193. float shadowCoord = gl_FragCoord.y - roundRect.y;
  194. float shadowValue = max(1. - (shadowCoord / shadow.x), 0.);
  195. float shadowShown = max(shadowValue * shadow.y, paused) * shadow.z;
  196. result = vec4(result.rgb * (1. - shadowShown), result.a);
  197. )" + round.body,
  198. };
  199. }
  200. [[nodiscard]] bool UseExpandForCamera(QSize original, QSize viewport) {
  201. using namespace ::Media::Streaming;
  202. return DecideFrameResize(viewport, original).expanding;
  203. }
  204. [[nodiscard]] QSize NonEmpty(QSize size) {
  205. return QSize(std::max(size.width(), 1), std::max(size.height(), 1));
  206. }
  207. [[nodiscard]] QSize CountBlurredSize(
  208. QSize unscaled,
  209. QSize outer,
  210. float factor) {
  211. factor *= kBlurTextureSizeFactor;
  212. const auto area = outer / int(base::SafeRound(factor * cScale() / 100));
  213. const auto scaled = unscaled.scaled(area, Qt::KeepAspectRatio);
  214. return (scaled.width() > unscaled.width()
  215. || scaled.height() > unscaled.height())
  216. ? unscaled
  217. : NonEmpty(scaled);
  218. }
  219. [[nodiscard]] QSize InterpolateScaledSize(
  220. QSize unscaled,
  221. QSize size,
  222. float64 ratio) {
  223. if (ratio == 0.) {
  224. return NonEmpty(unscaled.scaled(
  225. size,
  226. Qt::KeepAspectRatio));
  227. } else if (ratio == 1.) {
  228. return NonEmpty(unscaled.scaled(
  229. size,
  230. Qt::KeepAspectRatioByExpanding));
  231. }
  232. const auto notExpanded = NonEmpty(unscaled.scaled(
  233. size,
  234. Qt::KeepAspectRatio));
  235. const auto expanded = NonEmpty(unscaled.scaled(
  236. size,
  237. Qt::KeepAspectRatioByExpanding));
  238. return QSize(
  239. anim::interpolate(notExpanded.width(), expanded.width(), ratio),
  240. anim::interpolate(notExpanded.height(), expanded.height(), ratio));
  241. }
  242. [[nodiscard]] std::array<std::array<GLfloat, 2>, 4> CountTexCoords(
  243. QSize unscaled,
  244. QSize size,
  245. float64 expandRatio,
  246. bool swap = false) {
  247. const auto scaled = InterpolateScaledSize(unscaled, size, expandRatio);
  248. const auto left = (size.width() - scaled.width()) / 2;
  249. const auto top = (size.height() - scaled.height()) / 2;
  250. auto dleft = float(left) / scaled.width();
  251. auto dright = float(size.width() - left) / scaled.width();
  252. auto dtop = float(top) / scaled.height();
  253. auto dbottom = float(size.height() - top) / scaled.height();
  254. if (swap) {
  255. std::swap(dleft, dtop);
  256. std::swap(dright, dbottom);
  257. }
  258. return { {
  259. { { -dleft, 1.f + dtop } },
  260. { { dright, 1.f + dtop } },
  261. { { dright, 1.f - dbottom } },
  262. { { -dleft, 1.f - dbottom } },
  263. } };
  264. }
  265. } // namespace
  266. Viewport::RendererGL::RendererGL(not_null<Viewport*> owner)
  267. : _owner(owner)
  268. , _pinIcon(st::groupCallVideoTile.pin)
  269. , _muteIcon(st::groupCallVideoCrossLine)
  270. , _pinBackground(
  271. (st::groupCallVideoTile.pinPadding.top()
  272. + st::groupCallVideoTile.pin.icon.height()
  273. + st::groupCallVideoTile.pinPadding.bottom()) / 2,
  274. st::radialBg) {
  275. style::PaletteChanged(
  276. ) | rpl::start_with_next([=] {
  277. _buttons.invalidate();
  278. }, _lifetime);
  279. }
  280. void Viewport::RendererGL::init(
  281. not_null<QOpenGLWidget*> widget,
  282. QOpenGLFunctions &f) {
  283. _frameBuffer.emplace();
  284. _frameBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw);
  285. _frameBuffer->create();
  286. _frameBuffer->bind();
  287. _frameBuffer->allocate(kValues * sizeof(GLfloat));
  288. _downscaleProgram.yuv420.emplace();
  289. _downscaleVertexShader = LinkProgram(
  290. &*_downscaleProgram.yuv420,
  291. VertexShader({
  292. VertexPassTextureCoord(),
  293. }),
  294. FragmentShader({
  295. FragmentSampleYUV420Texture(),
  296. })).vertex;
  297. if (!_downscaleProgram.yuv420->isLinked()) {
  298. //...
  299. }
  300. _blurProgram.emplace();
  301. LinkProgram(
  302. &*_blurProgram,
  303. _downscaleVertexShader,
  304. FragmentShader({
  305. FragmentBlurTexture(false),
  306. }));
  307. _frameProgram.yuv420.emplace();
  308. _frameVertexShader = LinkProgram(
  309. &*_frameProgram.yuv420,
  310. VertexShader({
  311. VertexViewportTransform(),
  312. VertexPassTextureCoord(),
  313. VertexPassTextureCoord('b'),
  314. }),
  315. FragmentShader({
  316. FragmentSampleYUV420Texture(),
  317. FragmentFrameColor(),
  318. })).vertex;
  319. _imageProgram.emplace();
  320. LinkProgram(
  321. &*_imageProgram,
  322. VertexShader({
  323. VertexViewportTransform(),
  324. VertexPassTextureCoord(),
  325. }),
  326. FragmentShader({
  327. FragmentSampleARGB32Texture(),
  328. FragmentGlobalOpacity(),
  329. }));
  330. validateNoiseTexture(f, 0);
  331. }
  332. void Viewport::RendererGL::ensureARGB32Program() {
  333. Expects(_downscaleVertexShader != nullptr);
  334. Expects(_frameVertexShader != nullptr);
  335. _downscaleProgram.argb32.emplace();
  336. LinkProgram(
  337. &*_downscaleProgram.argb32,
  338. _downscaleVertexShader,
  339. FragmentShader({
  340. FragmentSampleARGB32Texture(),
  341. }));
  342. _frameProgram.argb32.emplace();
  343. LinkProgram(
  344. &*_frameProgram.argb32,
  345. _frameVertexShader,
  346. FragmentShader({
  347. FragmentSampleARGB32Texture(),
  348. FragmentFrameColor(),
  349. }));
  350. }
  351. void Viewport::RendererGL::deinit(
  352. not_null<QOpenGLWidget*> widget,
  353. QOpenGLFunctions *f) {
  354. _frameBuffer = std::nullopt;
  355. _frameVertexShader = nullptr;
  356. _imageProgram = std::nullopt;
  357. _downscaleProgram.argb32 = std::nullopt;
  358. _downscaleProgram.yuv420 = std::nullopt;
  359. _blurProgram = std::nullopt;
  360. _frameProgram.argb32 = std::nullopt;
  361. _frameProgram.yuv420 = std::nullopt;
  362. _noiseTexture.destroy(f);
  363. _noiseFramebuffer.destroy(f);
  364. for (auto &data : _tileData) {
  365. data.textures.destroy(f);
  366. }
  367. _tileData.clear();
  368. _tileDataIndices.clear();
  369. _buttons.destroy(f);
  370. }
  371. void Viewport::RendererGL::setDefaultViewport(QOpenGLFunctions &f) {
  372. f.glViewport(
  373. 0,
  374. 0,
  375. _viewport.width() * _factor,
  376. _viewport.height() * _factor);
  377. }
  378. void Viewport::RendererGL::paint(
  379. not_null<QOpenGLWidget*> widget,
  380. QOpenGLFunctions &f) {
  381. const auto factor = widget->devicePixelRatioF();
  382. if (_factor != factor) {
  383. _factor = factor;
  384. _ifactor = int(std::ceil(_factor));
  385. _buttons.invalidate();
  386. }
  387. _viewport = widget->size();
  388. const auto defaultFramebufferObject = widget->defaultFramebufferObject();
  389. validateDatas();
  390. auto index = 0;
  391. for (const auto &tile : _owner->_tiles) {
  392. if (!tile->visible()) {
  393. index++;
  394. continue;
  395. }
  396. paintTile(
  397. f,
  398. defaultFramebufferObject,
  399. tile.get(),
  400. _tileData[_tileDataIndices[index++]]);
  401. }
  402. }
  403. std::optional<QColor> Viewport::RendererGL::clearColor() {
  404. return st::groupCallBg->c;
  405. }
  406. void Viewport::RendererGL::validateUserpicFrame(
  407. not_null<VideoTile*> tile,
  408. TileData &tileData) {
  409. if (!_userpicFrame) {
  410. tileData.userpicFrame = QImage();
  411. return;
  412. } else if (!tileData.userpicFrame.isNull()) {
  413. return;
  414. }
  415. const auto size = tile->trackOrUserpicSize();
  416. tileData.userpicFrame = PeerData::GenerateUserpicImage(
  417. tile->row()->peer(),
  418. tile->row()->ensureUserpicView(),
  419. size.width(),
  420. 0);
  421. }
  422. void Viewport::RendererGL::paintTile(
  423. QOpenGLFunctions &f,
  424. GLuint defaultFramebufferObject,
  425. not_null<VideoTile*> tile,
  426. TileData &tileData) {
  427. const auto track = tile->track();
  428. const auto markGuard = gsl::finally([&] {
  429. tile->track()->markFrameShown();
  430. });
  431. const auto data = track->frameWithInfo(false);
  432. _userpicFrame = (data.format == Webrtc::FrameFormat::None);
  433. validateUserpicFrame(tile, tileData);
  434. const auto frameSize = _userpicFrame
  435. ? tileData.userpicFrame.size()
  436. : data.yuv420->size;
  437. const auto frameRotation = _userpicFrame
  438. ? 0
  439. : data.rotation;
  440. Assert(!frameSize.isEmpty());
  441. _rgbaFrame = (data.format == Webrtc::FrameFormat::ARGB32)
  442. || _userpicFrame;
  443. const auto geometry = tile->geometry();
  444. const auto x = geometry.x();
  445. const auto y = geometry.y();
  446. const auto width = geometry.width();
  447. const auto height = geometry.height();
  448. const auto &st = st::groupCallVideoTile;
  449. const auto shown = _owner->_controlsShownRatio;
  450. const auto fullscreen = _owner->_fullscreen;
  451. const auto fullNameShift = st.namePosition.y() + st::normalFont->height;
  452. const auto nameShift = anim::interpolate(fullNameShift, 0, shown);
  453. const auto row = tile->row();
  454. validateOutlineAnimation(tile, tileData);
  455. validatePausedAnimation(tile, tileData);
  456. const auto outline = tileData.outlined.value(tileData.outline ? 1. : 0.);
  457. const auto paused = tileData.paused.value(tileData.pause ? 1. : 0.);
  458. ensureButtonsImage();
  459. // Frame.
  460. const auto unscaled = Media::View::FlipSizeByRotation(
  461. frameSize,
  462. frameRotation);
  463. const auto tileSize = geometry.size();
  464. const auto swap = (((frameRotation / 90) % 2) == 1);
  465. const auto expand = isExpanded(tile, unscaled, tileSize);
  466. const auto animation = tile->animation();
  467. const auto expandRatio = (animation.ratio >= 0.)
  468. ? countExpandRatio(tile, unscaled, animation)
  469. : expand
  470. ? 1.
  471. : 0.;
  472. auto texCoords = CountTexCoords(unscaled, tileSize, expandRatio, swap);
  473. auto blurTexCoords = (expandRatio == 1. && !swap)
  474. ? texCoords
  475. : CountTexCoords(unscaled, tileSize, 1.);
  476. const auto rect = transformRect(geometry);
  477. auto toBlurTexCoords = std::array<std::array<GLfloat, 2>, 4> { {
  478. { { 0.f, 1.f } },
  479. { { 1.f, 1.f } },
  480. { { 1.f, 0.f } },
  481. { { 0.f, 0.f } },
  482. } };
  483. if (tile->mirror()) {
  484. std::swap(toBlurTexCoords[0], toBlurTexCoords[1]);
  485. std::swap(toBlurTexCoords[2], toBlurTexCoords[3]);
  486. std::swap(texCoords[0], texCoords[1]);
  487. std::swap(texCoords[2], texCoords[3]);
  488. }
  489. if (const auto shift = (frameRotation / 90); shift > 0) {
  490. std::rotate(
  491. toBlurTexCoords.begin(),
  492. toBlurTexCoords.begin() + shift,
  493. toBlurTexCoords.end());
  494. std::rotate(
  495. texCoords.begin(),
  496. texCoords.begin() + shift,
  497. texCoords.end());
  498. }
  499. const auto nameTop = y + (height
  500. - st.namePosition.y()
  501. - st::semiboldFont->height);
  502. // Paused icon and text.
  503. const auto middle = (st::groupCallVideoPlaceholderHeight
  504. - st::groupCallPaused.height()) / 2;
  505. const auto pausedSpace = (nameTop - y)
  506. - st::groupCallPaused.height()
  507. - st::semiboldFont->height;
  508. const auto pauseIconSkip = middle - st::groupCallVideoPlaceholderIconTop;
  509. const auto pauseTextSkip = st::groupCallVideoPlaceholderTextTop
  510. - st::groupCallVideoPlaceholderIconTop;
  511. const auto pauseIconTop = !_owner->wide()
  512. ? (y + (height - st::groupCallPaused.height()) / 2)
  513. : (pausedSpace < 3 * st::semiboldFont->height)
  514. ? (pausedSpace / 3)
  515. : std::min(
  516. y + (height / 2) - pauseIconSkip,
  517. (nameTop
  518. - st::semiboldFont->height * 3
  519. - st::groupCallPaused.height()));
  520. const auto pauseTextTop = (pausedSpace < 3 * st::semiboldFont->height)
  521. ? (nameTop - (pausedSpace / 3) - st::semiboldFont->height)
  522. : std::min(
  523. pauseIconTop + pauseTextSkip,
  524. nameTop - st::semiboldFont->height * 2);
  525. const auto pauseIcon = _buttons.texturedRect(
  526. QRect(
  527. x + (width - st::groupCallPaused.width()) / 2,
  528. pauseIconTop,
  529. st::groupCallPaused.width(),
  530. st::groupCallPaused.height()),
  531. _paused);
  532. const auto pauseRect = transformRect(pauseIcon.geometry);
  533. const auto factor = style::DevicePixelRatio();
  534. const auto pausedPosition = QPoint(
  535. x + (width - (_pausedTextRect.width() / factor)) / 2,
  536. pauseTextTop);
  537. const auto pausedText = _names.texturedRect(
  538. QRect(pausedPosition, _pausedTextRect.size() / factor),
  539. _pausedTextRect);
  540. const auto pausedRect = transformRect(pausedText.geometry);
  541. // Pin.
  542. const auto pin = _buttons.texturedRect(
  543. tile->pinInner().translated(x, y),
  544. tile->pinned() ? _pinOn : _pinOff,
  545. geometry);
  546. const auto pinRect = transformRect(pin.geometry);
  547. // Back.
  548. const auto back = _buttons.texturedRect(
  549. tile->backInner().translated(x, y),
  550. _back,
  551. geometry);
  552. const auto backRect = transformRect(back.geometry);
  553. // Mute.
  554. const auto &icon = st::groupCallVideoCrossLine.icon;
  555. const auto iconLeft = x + width - st.iconPosition.x() - icon.width();
  556. const auto iconTop = y + (height
  557. - st.iconPosition.y()
  558. - icon.height()
  559. + nameShift);
  560. const auto mute = _buttons.texturedRect(
  561. QRect(iconLeft, iconTop, icon.width(), icon.height()),
  562. (row->state() == MembersRow::State::Active
  563. ? _muteOff
  564. : _muteOn),
  565. geometry);
  566. const auto muteRect = transformRect(mute.geometry);
  567. // Name.
  568. const auto namePosition = QPoint(
  569. x + st.namePosition.x(),
  570. nameTop + nameShift);
  571. const auto name = _names.texturedRect(
  572. QRect(namePosition, tileData.nameRect.size() / factor),
  573. tileData.nameRect,
  574. geometry);
  575. const auto nameRect = transformRect(name.geometry);
  576. const GLfloat coords[] = {
  577. // YUV -> RGB-for-blur quad.
  578. -1.f, 1.f,
  579. toBlurTexCoords[0][0], toBlurTexCoords[0][1],
  580. 1.f, 1.f,
  581. toBlurTexCoords[1][0], toBlurTexCoords[1][1],
  582. 1.f, -1.f,
  583. toBlurTexCoords[2][0], toBlurTexCoords[2][1],
  584. -1.f, -1.f,
  585. toBlurTexCoords[3][0], toBlurTexCoords[3][1],
  586. // First RGB -> RGB blur pass.
  587. -1.f, 1.f,
  588. 0.f, 1.f,
  589. 1.f, 1.f,
  590. 1.f, 1.f,
  591. 1.f, -1.f,
  592. 1.f, 0.f,
  593. -1.f, -1.f,
  594. 0.f, 0.f,
  595. // Second blur pass + paint final frame.
  596. rect.left(), rect.top(),
  597. texCoords[0][0], texCoords[0][1],
  598. rect.right(), rect.top(),
  599. texCoords[1][0], texCoords[1][1],
  600. rect.right(), rect.bottom(),
  601. texCoords[2][0], texCoords[2][1],
  602. rect.left(), rect.bottom(),
  603. texCoords[3][0], texCoords[3][1],
  604. // Additional blurred background texture coordinates.
  605. blurTexCoords[0][0], blurTexCoords[0][1],
  606. blurTexCoords[1][0], blurTexCoords[1][1],
  607. blurTexCoords[2][0], blurTexCoords[2][1],
  608. blurTexCoords[3][0], blurTexCoords[3][1],
  609. // Pin button.
  610. pinRect.left(), pinRect.top(),
  611. pin.texture.left(), pin.texture.bottom(),
  612. pinRect.right(), pinRect.top(),
  613. pin.texture.right(), pin.texture.bottom(),
  614. pinRect.right(), pinRect.bottom(),
  615. pin.texture.right(), pin.texture.top(),
  616. pinRect.left(), pinRect.bottom(),
  617. pin.texture.left(), pin.texture.top(),
  618. // Back button.
  619. backRect.left(), backRect.top(),
  620. back.texture.left(), back.texture.bottom(),
  621. backRect.right(), backRect.top(),
  622. back.texture.right(), back.texture.bottom(),
  623. backRect.right(), backRect.bottom(),
  624. back.texture.right(), back.texture.top(),
  625. backRect.left(), backRect.bottom(),
  626. back.texture.left(), back.texture.top(),
  627. // Mute icon.
  628. muteRect.left(), muteRect.top(),
  629. mute.texture.left(), mute.texture.bottom(),
  630. muteRect.right(), muteRect.top(),
  631. mute.texture.right(), mute.texture.bottom(),
  632. muteRect.right(), muteRect.bottom(),
  633. mute.texture.right(), mute.texture.top(),
  634. muteRect.left(), muteRect.bottom(),
  635. mute.texture.left(), mute.texture.top(),
  636. // Name.
  637. nameRect.left(), nameRect.top(),
  638. name.texture.left(), name.texture.bottom(),
  639. nameRect.right(), nameRect.top(),
  640. name.texture.right(), name.texture.bottom(),
  641. nameRect.right(), nameRect.bottom(),
  642. name.texture.right(), name.texture.top(),
  643. nameRect.left(), nameRect.bottom(),
  644. name.texture.left(), name.texture.top(),
  645. // Paused icon.
  646. pauseRect.left(), pauseRect.top(),
  647. pauseIcon.texture.left(), pauseIcon.texture.bottom(),
  648. pauseRect.right(), pauseRect.top(),
  649. pauseIcon.texture.right(), pauseIcon.texture.bottom(),
  650. pauseRect.right(), pauseRect.bottom(),
  651. pauseIcon.texture.right(), pauseIcon.texture.top(),
  652. pauseRect.left(), pauseRect.bottom(),
  653. pauseIcon.texture.left(), pauseIcon.texture.top(),
  654. // Paused text.
  655. pausedRect.left(), pausedRect.top(),
  656. pausedText.texture.left(), pausedText.texture.bottom(),
  657. pausedRect.right(), pausedRect.top(),
  658. pausedText.texture.right(), pausedText.texture.bottom(),
  659. pausedRect.right(), pausedRect.bottom(),
  660. pausedText.texture.right(), pausedText.texture.top(),
  661. pausedRect.left(), pausedRect.bottom(),
  662. pausedText.texture.left(), pausedText.texture.top(),
  663. };
  664. _frameBuffer->bind();
  665. _frameBuffer->write(0, coords, sizeof(coords));
  666. const auto blurSize = CountBlurredSize(
  667. unscaled,
  668. geometry.size(),
  669. _factor);
  670. prepareObjects(f, tileData, blurSize);
  671. f.glViewport(0, 0, blurSize.width(), blurSize.height());
  672. bindFrame(f, data, tileData, _downscaleProgram);
  673. drawDownscalePass(f, tileData);
  674. drawFirstBlurPass(f, tileData, blurSize);
  675. f.glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject);
  676. setDefaultViewport(f);
  677. bindFrame(f, data, tileData, _frameProgram);
  678. const auto program = _rgbaFrame
  679. ? &*_frameProgram.argb32
  680. : &*_frameProgram.yuv420;
  681. const auto uniformViewport = QSizeF(_viewport) * _factor;
  682. program->bind();
  683. program->setUniformValue("viewport", uniformViewport);
  684. program->setUniformValue(
  685. "frameBg",
  686. fullscreen ? QColor(0, 0, 0) : st::groupCallBg->c);
  687. program->setUniformValue("radiusOutline", QVector2D(
  688. GLfloat(st::roundRadiusLarge * _factor * (fullscreen ? 0. : 1.)),
  689. (outline > 0) ? (st::groupCallOutline * _factor) : 0.f));
  690. program->setUniformValue("roundRect", Uniform(rect));
  691. program->setUniformValue("roundBg", st::groupCallBg->c);
  692. program->setUniformValue("outlineFg", QVector4D(
  693. st::groupCallMemberActiveIcon->c.redF(),
  694. st::groupCallMemberActiveIcon->c.greenF(),
  695. st::groupCallMemberActiveIcon->c.blueF(),
  696. st::groupCallMemberActiveIcon->c.alphaF() * outline));
  697. const auto shadowHeight = st.shadowHeight * _factor;
  698. const auto shadowAlpha = kShadowMaxAlpha / 255.f;
  699. program->setUniformValue("shadow", QVector4D(
  700. shadowHeight,
  701. shown,
  702. shadowAlpha,
  703. fullscreen ? 0. : kBlurOpacity));
  704. program->setUniformValue("paused", GLfloat(paused));
  705. f.glActiveTexture(_rgbaFrame ? GL_TEXTURE1 : GL_TEXTURE3);
  706. tileData.textures.bind(f, kFirstBlurPassTextureIndex);
  707. program->setUniformValue("b_texture", GLint(_rgbaFrame ? 1 : 3));
  708. f.glActiveTexture(_rgbaFrame ? GL_TEXTURE2 : GL_TEXTURE5);
  709. _noiseTexture.bind(f, 0);
  710. program->setUniformValue("n_texture", GLint(_rgbaFrame ? 2 : 5));
  711. program->setUniformValue(
  712. "texelOffset",
  713. GLfloat(1.f / blurSize.height()));
  714. GLint blurTexcoord = program->attributeLocation("b_texcoordIn");
  715. f.glVertexAttribPointer(
  716. blurTexcoord,
  717. 2,
  718. GL_FLOAT,
  719. GL_FALSE,
  720. 2 * sizeof(GLfloat),
  721. reinterpret_cast<const void*>(48 * sizeof(GLfloat)));
  722. f.glEnableVertexAttribArray(blurTexcoord);
  723. FillTexturedRectangle(f, program, 8);
  724. f.glDisableVertexAttribArray(blurTexcoord);
  725. const auto pinVisible = _owner->wide()
  726. && (pin.geometry.bottom() > y);
  727. const auto nameVisible = (nameShift != fullNameShift);
  728. const auto pausedVisible = (paused > 0.);
  729. if (!nameVisible && !pinVisible && !pausedVisible) {
  730. return;
  731. }
  732. f.glEnable(GL_BLEND);
  733. f.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  734. const auto guard = gsl::finally([&] {
  735. f.glDisable(GL_BLEND);
  736. });
  737. _imageProgram->bind();
  738. _imageProgram->setUniformValue("viewport", uniformViewport);
  739. _imageProgram->setUniformValue("s_texture", GLint(0));
  740. f.glActiveTexture(GL_TEXTURE0);
  741. _buttons.bind(f);
  742. // Paused icon.
  743. if (pausedVisible) {
  744. _imageProgram->setUniformValue("g_opacity", GLfloat(paused));
  745. FillTexturedRectangle(f, &*_imageProgram, 30);
  746. }
  747. _imageProgram->setUniformValue("g_opacity", GLfloat(1.f));
  748. // Pin.
  749. if (pinVisible) {
  750. FillTexturedRectangle(f, &*_imageProgram, 14);
  751. FillTexturedRectangle(f, &*_imageProgram, 18);
  752. }
  753. // Mute.
  754. if (nameVisible && !muteRect.empty()) {
  755. FillTexturedRectangle(f, &*_imageProgram, 22);
  756. }
  757. if (!nameVisible && !pausedVisible) {
  758. return;
  759. }
  760. _names.bind(f);
  761. // Name.
  762. if (nameVisible && !nameRect.empty()) {
  763. FillTexturedRectangle(f, &*_imageProgram, 26);
  764. }
  765. // Paused text.
  766. if (pausedVisible && _owner->wide()) {
  767. _imageProgram->setUniformValue("g_opacity", GLfloat(paused));
  768. FillTexturedRectangle(f, &*_imageProgram, 34);
  769. }
  770. }
  771. void Viewport::RendererGL::prepareObjects(
  772. QOpenGLFunctions &f,
  773. TileData &tileData,
  774. QSize blurSize) {
  775. if (!tileData.textures.created()) {
  776. tileData.textures.ensureCreated(f); // All are GL_LINEAR, except..
  777. tileData.textures.bind(f, kScaleForBlurTextureIndex);
  778. // kScaleForBlurTextureIndex is attached to framebuffer 0,
  779. // and is used to draw to framebuffer 1 of the same size.
  780. f.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  781. f.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  782. }
  783. tileData.framebuffers.ensureCreated(f);
  784. if (tileData.textureBlurSize == blurSize) {
  785. return;
  786. }
  787. tileData.textureBlurSize = blurSize;
  788. const auto create = [&](int framebufferIndex, int index) {
  789. tileData.textures.bind(f, index);
  790. f.glTexImage2D(
  791. GL_TEXTURE_2D,
  792. 0,
  793. GL_RGB,
  794. blurSize.width(),
  795. blurSize.height(),
  796. 0,
  797. GL_RGB,
  798. GL_UNSIGNED_BYTE,
  799. nullptr);
  800. tileData.framebuffers.bind(f, framebufferIndex);
  801. f.glFramebufferTexture2D(
  802. GL_FRAMEBUFFER,
  803. GL_COLOR_ATTACHMENT0,
  804. GL_TEXTURE_2D,
  805. tileData.textures.id(index),
  806. 0);
  807. };
  808. create(0, kScaleForBlurTextureIndex);
  809. create(1, kFirstBlurPassTextureIndex);
  810. }
  811. bool Viewport::RendererGL::isExpanded(
  812. not_null<VideoTile*> tile,
  813. QSize unscaled,
  814. QSize tileSize) const {
  815. return !tile->screencast()
  816. && (!_owner->wide() || UseExpandForCamera(unscaled, tileSize));
  817. }
  818. float64 Viewport::RendererGL::countExpandRatio(
  819. not_null<VideoTile*> tile,
  820. QSize unscaled,
  821. const TileAnimation &animation) const {
  822. const auto expandedFrom = isExpanded(tile, unscaled, animation.from);
  823. const auto expandedTo = isExpanded(tile, unscaled, animation.to);
  824. return (expandedFrom && expandedTo)
  825. ? 1.
  826. : (!expandedFrom && !expandedTo)
  827. ? 0.
  828. : expandedFrom
  829. ? (1. - animation.ratio)
  830. : animation.ratio;
  831. }
  832. void Viewport::RendererGL::bindFrame(
  833. QOpenGLFunctions &f,
  834. const Webrtc::FrameWithInfo &data,
  835. TileData &tileData,
  836. Program &program) {
  837. const auto imageIndex = _userpicFrame ? 0 : (data.index + 1);
  838. const auto upload = (tileData.trackIndex != imageIndex);
  839. tileData.trackIndex = imageIndex;
  840. if (_rgbaFrame) {
  841. ensureARGB32Program();
  842. program.argb32->bind();
  843. f.glActiveTexture(GL_TEXTURE0);
  844. tileData.textures.bind(f, 0);
  845. if (upload) {
  846. const auto &image = _userpicFrame
  847. ? tileData.userpicFrame
  848. : data.original;
  849. const auto stride = image.bytesPerLine() / 4;
  850. const auto data = image.constBits();
  851. uploadTexture(
  852. f,
  853. Ui::GL::kFormatRGBA,
  854. Ui::GL::kFormatRGBA,
  855. image.size(),
  856. tileData.rgbaSize,
  857. stride,
  858. data);
  859. tileData.rgbaSize = image.size();
  860. tileData.textureSize = QSize();
  861. }
  862. program.argb32->setUniformValue("s_texture", GLint(0));
  863. } else {
  864. const auto yuv = data.yuv420;
  865. program.yuv420->bind();
  866. f.glActiveTexture(GL_TEXTURE0);
  867. tileData.textures.bind(f, 0);
  868. if (upload) {
  869. f.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  870. uploadTexture(
  871. f,
  872. GL_ALPHA,
  873. GL_ALPHA,
  874. yuv->size,
  875. tileData.textureSize,
  876. yuv->y.stride,
  877. yuv->y.data);
  878. tileData.textureSize = yuv->size;
  879. tileData.rgbaSize = QSize();
  880. }
  881. f.glActiveTexture(GL_TEXTURE1);
  882. tileData.textures.bind(f, 1);
  883. if (upload) {
  884. uploadTexture(
  885. f,
  886. GL_ALPHA,
  887. GL_ALPHA,
  888. yuv->chromaSize,
  889. tileData.textureChromaSize,
  890. yuv->u.stride,
  891. yuv->u.data);
  892. }
  893. f.glActiveTexture(GL_TEXTURE2);
  894. tileData.textures.bind(f, 2);
  895. if (upload) {
  896. uploadTexture(
  897. f,
  898. GL_ALPHA,
  899. GL_ALPHA,
  900. yuv->chromaSize,
  901. tileData.textureChromaSize,
  902. yuv->v.stride,
  903. yuv->v.data);
  904. tileData.textureChromaSize = yuv->chromaSize;
  905. f.glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
  906. }
  907. program.yuv420->setUniformValue("y_texture", GLint(0));
  908. program.yuv420->setUniformValue("u_texture", GLint(1));
  909. program.yuv420->setUniformValue("v_texture", GLint(2));
  910. }
  911. }
  912. void Viewport::RendererGL::uploadTexture(
  913. QOpenGLFunctions &f,
  914. GLint internalformat,
  915. GLint format,
  916. QSize size,
  917. QSize hasSize,
  918. int stride,
  919. const void *data) const {
  920. f.glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
  921. if (hasSize != size) {
  922. f.glTexImage2D(
  923. GL_TEXTURE_2D,
  924. 0,
  925. internalformat,
  926. size.width(),
  927. size.height(),
  928. 0,
  929. format,
  930. GL_UNSIGNED_BYTE,
  931. data);
  932. } else {
  933. f.glTexSubImage2D(
  934. GL_TEXTURE_2D,
  935. 0,
  936. 0,
  937. 0,
  938. size.width(),
  939. size.height(),
  940. format,
  941. GL_UNSIGNED_BYTE,
  942. data);
  943. }
  944. f.glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
  945. }
  946. void Viewport::RendererGL::drawDownscalePass(
  947. QOpenGLFunctions &f,
  948. TileData &tileData) {
  949. tileData.framebuffers.bind(f, 0);
  950. const auto program = _rgbaFrame
  951. ? &*_downscaleProgram.argb32
  952. : &*_downscaleProgram.yuv420;
  953. program->bind();
  954. FillTexturedRectangle(f, program);
  955. }
  956. void Viewport::RendererGL::drawFirstBlurPass(
  957. QOpenGLFunctions &f,
  958. TileData &tileData,
  959. QSize blurSize) {
  960. tileData.framebuffers.bind(f, 1);
  961. _blurProgram->bind();
  962. f.glActiveTexture(GL_TEXTURE0);
  963. tileData.textures.bind(f, kScaleForBlurTextureIndex);
  964. _blurProgram->setUniformValue("b_texture", GLint(0));
  965. _blurProgram->setUniformValue(
  966. "texelOffset",
  967. GLfloat(1.f / blurSize.width()));
  968. FillTexturedRectangle(f, &*_blurProgram, 4);
  969. }
  970. Rect Viewport::RendererGL::transformRect(const Rect &raster) const {
  971. return TransformRect(raster, _viewport, _factor);
  972. }
  973. Rect Viewport::RendererGL::transformRect(const QRect &raster) const {
  974. return TransformRect(Rect(raster), _viewport, _factor);
  975. }
  976. void Viewport::RendererGL::ensureButtonsImage() {
  977. if (_buttons) {
  978. return;
  979. }
  980. const auto pinOnSize = VideoTile::PinInnerSize(true);
  981. const auto pinOffSize = VideoTile::PinInnerSize(false);
  982. const auto backSize = VideoTile::BackInnerSize();
  983. const auto muteSize = st::groupCallVideoCrossLine.icon.size();
  984. const auto pausedSize = st::groupCallPaused.size();
  985. const auto fullSize = QSize(
  986. std::max({
  987. pinOnSize.width(),
  988. pinOffSize.width(),
  989. backSize.width(),
  990. 2 * muteSize.width(),
  991. pausedSize.width(),
  992. }),
  993. (pinOnSize.height()
  994. + pinOffSize.height()
  995. + backSize.height()
  996. + muteSize.height()
  997. + pausedSize.height()));
  998. const auto imageSize = fullSize * _ifactor;
  999. auto image = _buttons.takeImage();
  1000. if (image.size() != imageSize) {
  1001. image = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
  1002. }
  1003. image.fill(Qt::transparent);
  1004. image.setDevicePixelRatio(_ifactor);
  1005. {
  1006. auto p = Painter(&image);
  1007. auto hq = PainterHighQualityEnabler(p);
  1008. _pinOn = QRect(QPoint(), pinOnSize * _ifactor);
  1009. VideoTile::PaintPinButton(
  1010. p,
  1011. true,
  1012. 0,
  1013. 0,
  1014. fullSize.width(),
  1015. &_pinBackground,
  1016. &_pinIcon);
  1017. const auto pinOffTop = pinOnSize.height();
  1018. _pinOff = QRect(
  1019. QPoint(0, pinOffTop) * _ifactor,
  1020. pinOffSize * _ifactor);
  1021. VideoTile::PaintPinButton(
  1022. p,
  1023. false,
  1024. 0,
  1025. pinOnSize.height(),
  1026. fullSize.width(),
  1027. &_pinBackground,
  1028. &_pinIcon);
  1029. const auto backTop = pinOffTop + pinOffSize.height();
  1030. _back = QRect(QPoint(0, backTop) * _ifactor, backSize * _ifactor);
  1031. VideoTile::PaintBackButton(
  1032. p,
  1033. 0,
  1034. pinOnSize.height() + pinOffSize.height(),
  1035. fullSize.width(),
  1036. &_pinBackground);
  1037. const auto muteTop = backTop + backSize.height();
  1038. _muteOn = QRect(QPoint(0, muteTop) * _ifactor, muteSize * _ifactor);
  1039. _muteIcon.paint(p, { 0, muteTop }, 1.);
  1040. _muteOff = QRect(
  1041. QPoint(muteSize.width(), muteTop) * _ifactor,
  1042. muteSize * _ifactor);
  1043. _muteIcon.paint(p, { muteSize.width(), muteTop }, 0.);
  1044. const auto pausedTop = muteTop + muteSize.height();
  1045. _paused = QRect(
  1046. QPoint(0, pausedTop) * _ifactor,
  1047. pausedSize * _ifactor);
  1048. st::groupCallPaused.paint(p, 0, pausedTop, fullSize.width());
  1049. }
  1050. _buttons.setImage(std::move(image));
  1051. }
  1052. void Viewport::RendererGL::validateDatas() {
  1053. const auto &tiles = _owner->_tiles;
  1054. const auto &st = st::groupCallVideoTile;
  1055. const auto count = int(tiles.size());
  1056. const auto factor = style::DevicePixelRatio();
  1057. const auto nameHeight = st::semiboldFont->height * factor;
  1058. const auto pausedText = tr::lng_group_call_video_paused(tr::now);
  1059. const auto pausedBottom = nameHeight;
  1060. const auto pausedWidth = st::semiboldFont->width(pausedText) * factor;
  1061. struct Request {
  1062. int index = 0;
  1063. bool updating = false;
  1064. };
  1065. auto requests = std::vector<Request>();
  1066. auto available = std::max(_names.image().width(), pausedWidth);
  1067. for (auto &data : _tileData) {
  1068. data.stale = true;
  1069. }
  1070. _tileDataIndices.resize(count);
  1071. const auto nameWidth = [&](int i) {
  1072. const auto row = tiles[i]->row();
  1073. const auto hasWidth = tiles[i]->geometry().width()
  1074. - st.iconPosition.x()
  1075. - st::groupCallVideoCrossLine.icon.width()
  1076. - st.namePosition.x();
  1077. if (hasWidth < 1) {
  1078. return 0;
  1079. }
  1080. return std::clamp(row->name().maxWidth(), 1, hasWidth) * factor;
  1081. };
  1082. for (auto i = 0; i != count; ++i) {
  1083. tiles[i]->row()->lazyInitialize(st::groupCallMembersListItem);
  1084. const auto width = nameWidth(i);
  1085. if (width > available) {
  1086. available = width;
  1087. }
  1088. const auto id = quintptr(tiles[i]->track().get());
  1089. const auto j = ranges::find(_tileData, id, &TileData::id);
  1090. if (j != end(_tileData)) {
  1091. j->stale = false;
  1092. const auto index = (j - begin(_tileData));
  1093. _tileDataIndices[i] = index;
  1094. const auto peer = tiles[i]->row()->peer();
  1095. if ((j->peer != peer)
  1096. || (j->nameVersion != peer->nameVersion())
  1097. || (j->nameRect.width() != width)) {
  1098. const auto nameTop = pausedBottom + index * nameHeight;
  1099. j->nameRect = QRect(0, nameTop, width, nameHeight);
  1100. requests.push_back({ .index = i, .updating = true });
  1101. }
  1102. } else {
  1103. _tileDataIndices[i] = -1;
  1104. requests.push_back({ .index = i, .updating = false });
  1105. }
  1106. }
  1107. if (requests.empty()) {
  1108. return;
  1109. }
  1110. auto maybeStaleAfter = begin(_tileData);
  1111. auto maybeStaleEnd = end(_tileData);
  1112. for (auto &request : requests) {
  1113. const auto i = request.index;
  1114. if (_tileDataIndices[i] >= 0) {
  1115. continue;
  1116. }
  1117. const auto id = quintptr(tiles[i]->track().get());
  1118. const auto peer = tiles[i]->row()->peer();
  1119. const auto paused = (tiles[i]->track()->state()
  1120. == Webrtc::VideoState::Paused);
  1121. auto index = int(_tileData.size());
  1122. maybeStaleAfter = ranges::find(
  1123. maybeStaleAfter,
  1124. maybeStaleEnd,
  1125. true,
  1126. &TileData::stale);
  1127. if (maybeStaleAfter != maybeStaleEnd) {
  1128. index = (maybeStaleAfter - begin(_tileData));
  1129. maybeStaleAfter->id = id;
  1130. maybeStaleAfter->peer = peer;
  1131. maybeStaleAfter->stale = false;
  1132. maybeStaleAfter->pause = paused;
  1133. maybeStaleAfter->paused.stop();
  1134. request.updating = true;
  1135. } else {
  1136. // This invalidates maybeStale*, but they're already equal.
  1137. _tileData.push_back({
  1138. .id = id,
  1139. .peer = peer,
  1140. .pause = paused,
  1141. });
  1142. }
  1143. const auto nameTop = pausedBottom + index * nameHeight;
  1144. _tileData[index].nameVersion = peer->nameVersion();
  1145. _tileData[index].nameRect = QRect(
  1146. 0,
  1147. nameTop,
  1148. nameWidth(i),
  1149. nameHeight);
  1150. _tileDataIndices[i] = index;
  1151. }
  1152. auto image = _names.takeImage();
  1153. const auto imageSize = QSize(
  1154. available,
  1155. pausedBottom + _tileData.size() * nameHeight);
  1156. const auto allocate = (image.size() != imageSize);
  1157. auto paintToImage = allocate
  1158. ? QImage(imageSize, QImage::Format_ARGB32_Premultiplied)
  1159. : base::take(image);
  1160. paintToImage.setDevicePixelRatio(factor);
  1161. if (allocate && image.isNull()) {
  1162. paintToImage.fill(Qt::transparent);
  1163. }
  1164. {
  1165. auto p = Painter(&paintToImage);
  1166. p.setPen(st::groupCallVideoTextFg);
  1167. if (!image.isNull()) {
  1168. p.setCompositionMode(QPainter::CompositionMode_Source);
  1169. p.drawImage(0, 0, image);
  1170. if (paintToImage.width() > image.width()) {
  1171. p.fillRect(
  1172. image.width() / factor,
  1173. 0,
  1174. (paintToImage.width() - image.width()) / factor,
  1175. image.height() / factor,
  1176. Qt::transparent);
  1177. }
  1178. if (paintToImage.height() > image.height()) {
  1179. p.fillRect(
  1180. 0,
  1181. image.height() / factor,
  1182. paintToImage.width() / factor,
  1183. (paintToImage.height() - image.height()) / factor,
  1184. Qt::transparent);
  1185. }
  1186. p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  1187. } else if (allocate) {
  1188. p.setFont(st::semiboldFont);
  1189. p.drawText(0, st::semiboldFont->ascent, pausedText);
  1190. _pausedTextRect = QRect(0, 0, pausedWidth, nameHeight);
  1191. }
  1192. for (const auto &request : requests) {
  1193. const auto i = request.index;
  1194. const auto &data = _tileData[_tileDataIndices[i]];
  1195. if (data.nameRect.isEmpty()) {
  1196. continue;
  1197. }
  1198. const auto row = tiles[i]->row();
  1199. if (request.updating) {
  1200. p.setCompositionMode(QPainter::CompositionMode_Source);
  1201. p.fillRect(
  1202. 0,
  1203. data.nameRect.y() / factor,
  1204. paintToImage.width() / factor,
  1205. nameHeight / factor,
  1206. Qt::transparent);
  1207. p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  1208. }
  1209. row->name().drawLeftElided(
  1210. p,
  1211. 0,
  1212. data.nameRect.y() / factor,
  1213. data.nameRect.width() / factor,
  1214. paintToImage.width() / factor);
  1215. }
  1216. }
  1217. _names.setImage(std::move(paintToImage));
  1218. }
  1219. void Viewport::RendererGL::validateNoiseTexture(
  1220. QOpenGLFunctions &f,
  1221. GLuint defaultFramebufferObject) {
  1222. if (_noiseTexture.created()) {
  1223. return;
  1224. }
  1225. _noiseTexture.ensureCreated(f, GL_NEAREST, GL_REPEAT);
  1226. _noiseTexture.bind(f, 0);
  1227. // Rendering to GL_ALPHA is not supported.
  1228. f.glTexImage2D(
  1229. GL_TEXTURE_2D,
  1230. 0,
  1231. GL_R8,
  1232. kNoiseTextureSize,
  1233. kNoiseTextureSize,
  1234. 0,
  1235. GL_RED,
  1236. GL_UNSIGNED_BYTE,
  1237. nullptr);
  1238. if (f.glGetError() != GL_NO_ERROR) {
  1239. // Direct3D 9 doesn't support GL_R8 textures.
  1240. f.glTexImage2D(
  1241. GL_TEXTURE_2D,
  1242. 0,
  1243. GL_RGB,
  1244. kNoiseTextureSize,
  1245. kNoiseTextureSize,
  1246. 0,
  1247. GL_RGB,
  1248. GL_UNSIGNED_BYTE,
  1249. nullptr);
  1250. }
  1251. _noiseFramebuffer.ensureCreated(f);
  1252. _noiseFramebuffer.bind(f, 0);
  1253. f.glFramebufferTexture2D(
  1254. GL_FRAMEBUFFER,
  1255. GL_COLOR_ATTACHMENT0,
  1256. GL_TEXTURE_2D,
  1257. _noiseTexture.id(0),
  1258. 0);
  1259. f.glViewport(0, 0, kNoiseTextureSize, kNoiseTextureSize);
  1260. const GLfloat coords[] = {
  1261. -1, -1,
  1262. -1, 1,
  1263. 1, 1,
  1264. 1, -1,
  1265. };
  1266. auto buffer = QOpenGLBuffer();
  1267. buffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
  1268. buffer.create();
  1269. buffer.bind();
  1270. buffer.allocate(coords, sizeof(coords));
  1271. auto program = QOpenGLShaderProgram();
  1272. LinkProgram(
  1273. &program,
  1274. VertexShader({}),
  1275. FragmentShader({ FragmentGenerateNoise() }));
  1276. program.bind();
  1277. GLint position = program.attributeLocation("position");
  1278. f.glVertexAttribPointer(
  1279. position,
  1280. 2,
  1281. GL_FLOAT,
  1282. GL_FALSE,
  1283. 2 * sizeof(GLfloat),
  1284. nullptr);
  1285. f.glEnableVertexAttribArray(position);
  1286. f.glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  1287. f.glDisableVertexAttribArray(position);
  1288. f.glUseProgram(0);
  1289. }
  1290. void Viewport::RendererGL::validateOutlineAnimation(
  1291. not_null<VideoTile*> tile,
  1292. TileData &data) {
  1293. const auto outline = tile->row()->speaking();
  1294. if (data.outline == outline) {
  1295. return;
  1296. }
  1297. data.outline = outline;
  1298. data.outlined.start(
  1299. [=] { _owner->widget()->update(); },
  1300. outline ? 0. : 1.,
  1301. outline ? 1. : 0.,
  1302. st::fadeWrapDuration);
  1303. }
  1304. void Viewport::RendererGL::validatePausedAnimation(
  1305. not_null<VideoTile*> tile,
  1306. TileData &data) {
  1307. const auto paused = (_userpicFrame
  1308. && tile->track()->frameSize().isEmpty())
  1309. || (tile->track()->state() == Webrtc::VideoState::Paused);
  1310. if (data.pause == paused) {
  1311. return;
  1312. }
  1313. data.pause = paused;
  1314. data.paused.start(
  1315. [=] { _owner->widget()->update(); },
  1316. paused ? 0. : 1.,
  1317. paused ? 1. : 0.,
  1318. st::fadeWrapDuration);
  1319. }
  1320. } // namespace Calls::Group