calls_userpic.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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/calls_userpic.h"
  8. #include "data/data_peer.h"
  9. #include "main/main_session.h"
  10. #include "data/data_changes.h"
  11. #include "data/data_peer.h"
  12. #include "data/data_session.h"
  13. #include "data/data_cloud_file.h"
  14. #include "data/data_photo_media.h"
  15. #include "data/data_file_origin.h"
  16. #include "ui/empty_userpic.h"
  17. #include "ui/painter.h"
  18. #include "apiwrap.h" // requestFullPeer.
  19. #include "styles/style_calls.h"
  20. namespace Calls {
  21. namespace {
  22. } // namespace
  23. Userpic::Userpic(
  24. not_null<QWidget*> parent,
  25. not_null<PeerData*> peer,
  26. rpl::producer<bool> muted)
  27. : _content(parent)
  28. , _peer(peer) {
  29. setGeometry(0, 0, 0);
  30. setup(std::move(muted));
  31. }
  32. Userpic::~Userpic() = default;
  33. void Userpic::setVisible(bool visible) {
  34. _content.setVisible(visible);
  35. }
  36. void Userpic::setGeometry(int x, int y, int size) {
  37. if (this->size() != size) {
  38. _userPhoto = QPixmap();
  39. _userPhotoFull = false;
  40. }
  41. _content.setGeometry(x, y, size, size);
  42. _content.update();
  43. if (_userPhoto.isNull()) {
  44. refreshPhoto();
  45. }
  46. }
  47. void Userpic::setup(rpl::producer<bool> muted) {
  48. _content.show();
  49. _content.setAttribute(Qt::WA_TransparentForMouseEvents);
  50. _content.paintRequest(
  51. ) | rpl::start_with_next([=] {
  52. paint();
  53. }, lifetime());
  54. std::move(
  55. muted
  56. ) | rpl::start_with_next([=](bool muted) {
  57. setMuted(muted);
  58. }, lifetime());
  59. _peer->session().changes().peerFlagsValue(
  60. _peer,
  61. Data::PeerUpdate::Flag::Photo
  62. ) | rpl::start_with_next([=] {
  63. processPhoto();
  64. }, lifetime());
  65. _peer->session().downloaderTaskFinished(
  66. ) | rpl::start_with_next([=] {
  67. refreshPhoto();
  68. }, lifetime());
  69. _mutedAnimation.stop();
  70. }
  71. void Userpic::setMuteLayout(QPoint position, int size, int stroke) {
  72. _mutePosition = position;
  73. _muteSize = size;
  74. _muteStroke = stroke;
  75. _content.update();
  76. }
  77. void Userpic::paint() {
  78. auto p = QPainter(&_content);
  79. p.drawPixmap(0, 0, _userPhoto);
  80. if (_muted && _muteSize > 0) {
  81. auto hq = PainterHighQualityEnabler(p);
  82. auto pen = st::callBgOpaque->p;
  83. pen.setWidth(_muteStroke);
  84. p.setPen(pen);
  85. p.setBrush(st::callHangupBg);
  86. const auto rect = QRect(
  87. _mutePosition.x() - _muteSize / 2,
  88. _mutePosition.y() - _muteSize / 2,
  89. _muteSize,
  90. _muteSize);
  91. p.drawEllipse(rect);
  92. st::callMutedPeerIcon.paintInCenter(p, rect);
  93. }
  94. }
  95. void Userpic::setMuted(bool muted) {
  96. if (_muted == muted) {
  97. return;
  98. }
  99. _muted = muted;
  100. _content.update();
  101. //_mutedAnimation.start(
  102. // [=] { _content.update(); },
  103. // _muted ? 0. : 1.,
  104. // _muted ? 1. : 0.,
  105. // st::fadeWrapDuration);
  106. }
  107. int Userpic::size() const {
  108. return _content.width();
  109. }
  110. void Userpic::processPhoto() {
  111. _userpic = _peer->createUserpicView();
  112. _peer->loadUserpic();
  113. const auto photo = _peer->userpicPhotoId()
  114. ? _peer->owner().photo(_peer->userpicPhotoId()).get()
  115. : nullptr;
  116. if (isGoodPhoto(photo)) {
  117. _photo = photo->createMediaView();
  118. _photo->wanted(Data::PhotoSize::Thumbnail, _peer->userpicPhotoOrigin());
  119. } else {
  120. _photo = nullptr;
  121. if (_peer->userpicPhotoUnknown() || (photo && photo->isNull())) {
  122. _peer->session().api().requestFullPeer(_peer);
  123. }
  124. }
  125. refreshPhoto();
  126. }
  127. void Userpic::refreshPhoto() {
  128. if (!size()) {
  129. return;
  130. }
  131. const auto isNewBigPhoto = [&] {
  132. return _photo
  133. && (_photo->image(Data::PhotoSize::Thumbnail) != nullptr)
  134. && (_photo->owner()->id != _userPhotoId || !_userPhotoFull);
  135. }();
  136. if (isNewBigPhoto) {
  137. _userPhotoId = _photo->owner()->id;
  138. _userPhotoFull = true;
  139. createCache(_photo->image(Data::PhotoSize::Thumbnail));
  140. } else if (_userPhoto.isNull()) {
  141. if (const auto cloud = _peer->userpicCloudImage(_userpic)) {
  142. auto image = Image(base::duplicate(*cloud));
  143. createCache(&image);
  144. } else {
  145. createCache(nullptr);
  146. }
  147. }
  148. }
  149. void Userpic::createCache(Image *image) {
  150. const auto size = this->size();
  151. const auto real = size * style::DevicePixelRatio();
  152. //_useTransparency
  153. // ? (Images::Option::RoundLarge
  154. // | Images::Option::RoundSkipBottomLeft
  155. // | Images::Option::RoundSkipBottomRight)
  156. // : Images::Option::None;
  157. if (image) {
  158. auto width = image->width();
  159. auto height = image->height();
  160. if (width > height) {
  161. width = qMax((width * real) / height, 1);
  162. height = real;
  163. } else {
  164. height = qMax((height * real) / width, 1);
  165. width = real;
  166. }
  167. _userPhoto = image->pixNoCache(
  168. { width, height },
  169. {
  170. .options = Images::Option::RoundCircle,
  171. .outer = { size, size },
  172. });
  173. _userPhoto.setDevicePixelRatio(style::DevicePixelRatio());
  174. } else {
  175. auto filled = QImage(
  176. QSize(real, real),
  177. QImage::Format_ARGB32_Premultiplied);
  178. filled.setDevicePixelRatio(style::DevicePixelRatio());
  179. filled.fill(Qt::transparent);
  180. {
  181. auto p = QPainter(&filled);
  182. Ui::EmptyUserpic(
  183. Ui::EmptyUserpic::UserpicColor(_peer->colorIndex()),
  184. _peer->name()
  185. ).paintCircle(p, 0, 0, size, size);
  186. }
  187. //_userPhoto = Images::PixmapFast(Images::Round(
  188. // std::move(filled),
  189. // ImageRoundRadius::Large,
  190. // RectPart::TopLeft | RectPart::TopRight));
  191. _userPhoto = Images::PixmapFast(std::move(filled));
  192. }
  193. _content.update();
  194. }
  195. bool Userpic::isGoodPhoto(PhotoData *photo) const {
  196. if (!photo || photo->isNull()) {
  197. return false;
  198. }
  199. const auto badAspect = [](int a, int b) {
  200. return a > 10 * b;
  201. };
  202. const auto width = photo->width();
  203. const auto height = photo->height();
  204. return !badAspect(width, height) && !badAspect(height, width);
  205. }
  206. } // namespace Calls