attach_single_media_preview.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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 "ui/chat/attach/attach_single_media_preview.h"
  8. #include "editor/photo_editor_common.h"
  9. #include "ui/chat/attach/attach_prepare.h"
  10. #include "core/mime_type.h"
  11. #include "lottie/lottie_single_player.h"
  12. namespace Ui {
  13. SingleMediaPreview *SingleMediaPreview::Create(
  14. QWidget *parent,
  15. const style::ComposeControls &st,
  16. Fn<bool()> gifPaused,
  17. const PreparedFile &file,
  18. Fn<bool(AttachActionType)> actionAllowed,
  19. AttachControls::Type type) {
  20. auto preview = QImage();
  21. auto animated = false;
  22. auto animationPreview = false;
  23. auto hasModifications = false;
  24. if (const auto image = std::get_if<PreparedFileInformation::Image>(
  25. &file.information->media)) {
  26. preview = Editor::ImageModified(image->data, image->modifications);
  27. animated = animationPreview = image->animated;
  28. hasModifications = !image->modifications.empty();
  29. } else if (const auto video = std::get_if<PreparedFileInformation::Video>(
  30. &file.information->media)) {
  31. preview = file.videoCover
  32. ? file.videoCover->preview
  33. : video->thumbnail;
  34. animated = true;
  35. animationPreview = video->isGifv;
  36. }
  37. if (preview.isNull()) {
  38. return nullptr;
  39. } else if (!animated
  40. && !ValidateThumbDimensions(preview.width(), preview.height())
  41. && !hasModifications) {
  42. return nullptr;
  43. }
  44. return CreateChild<SingleMediaPreview>(
  45. parent,
  46. st,
  47. std::move(gifPaused),
  48. preview,
  49. animated,
  50. Core::IsMimeSticker(file.information->filemime),
  51. file.spoiler,
  52. animationPreview ? file.path : QString(),
  53. type,
  54. std::move(actionAllowed));
  55. }
  56. SingleMediaPreview::SingleMediaPreview(
  57. QWidget *parent,
  58. const style::ComposeControls &st,
  59. Fn<bool()> gifPaused,
  60. QImage preview,
  61. bool animated,
  62. bool sticker,
  63. bool spoiler,
  64. const QString &animatedPreviewPath,
  65. AttachControls::Type type,
  66. Fn<bool(AttachActionType)> actionAllowed)
  67. : AbstractSingleMediaPreview(parent, st, type, std::move(actionAllowed))
  68. , _gifPaused(std::move(gifPaused))
  69. , _sticker(sticker) {
  70. Expects(!preview.isNull());
  71. setAnimated(animated);
  72. preparePreview(preview);
  73. prepareAnimatedPreview(animatedPreviewPath, animated);
  74. setSpoiler(spoiler);
  75. }
  76. bool SingleMediaPreview::supportsSpoilers() const {
  77. return !_sticker || sendWay().sendImagesAsPhotos();
  78. }
  79. bool SingleMediaPreview::drawBackground() const {
  80. return !_sticker;
  81. }
  82. bool SingleMediaPreview::tryPaintAnimation(QPainter &p) {
  83. if (_gifPreview && _gifPreview->started()) {
  84. const auto paused = _gifPaused();
  85. const auto frame = _gifPreview->current({
  86. .frame = QSize(previewWidth(), previewHeight()),
  87. }, paused ? 0 : crl::now());
  88. p.drawImage(previewLeft(), previewTop(), frame);
  89. return true;
  90. } else if (_lottiePreview && _lottiePreview->ready()) {
  91. const auto frame = _lottiePreview->frame();
  92. const auto size = frame.size() / style::DevicePixelRatio();
  93. p.drawImage(
  94. QRect(
  95. previewLeft() + (previewWidth() - size.width()) / 2,
  96. (previewHeight() - size.height()) / 2,
  97. size.width(),
  98. size.height()),
  99. frame);
  100. _lottiePreview->markFrameShown();
  101. return true;
  102. }
  103. return false;
  104. }
  105. bool SingleMediaPreview::isAnimatedPreviewReady() const {
  106. return _gifPreview || _lottiePreview;
  107. }
  108. void SingleMediaPreview::prepareAnimatedPreview(
  109. const QString &animatedPreviewPath,
  110. bool animated) {
  111. if (_sticker && animated) {
  112. const auto box = QSize(previewWidth(), previewHeight())
  113. * style::DevicePixelRatio();
  114. _lottiePreview = std::make_unique<Lottie::SinglePlayer>(
  115. Lottie::ReadContent(QByteArray(), animatedPreviewPath),
  116. Lottie::FrameRequest{ box });
  117. _lottiePreview->updates(
  118. ) | rpl::start_with_next([=] {
  119. update();
  120. }, lifetime());
  121. } else if (!animatedPreviewPath.isEmpty()) {
  122. auto callback = [=](Media::Clip::Notification notification) {
  123. clipCallback(notification);
  124. };
  125. _gifPreview = Media::Clip::MakeReader(
  126. animatedPreviewPath,
  127. std::move(callback));
  128. }
  129. }
  130. void SingleMediaPreview::clipCallback(
  131. Media::Clip::Notification notification) {
  132. using namespace Media::Clip;
  133. switch (notification) {
  134. case Notification::Reinit: {
  135. if (_gifPreview && _gifPreview->state() == State::Error) {
  136. _gifPreview.setBad();
  137. }
  138. if (_gifPreview && _gifPreview->ready() && !_gifPreview->started()) {
  139. _gifPreview->start({
  140. .frame = QSize(previewWidth(), previewHeight()),
  141. });
  142. }
  143. update();
  144. } break;
  145. case Notification::Repaint: {
  146. if (_gifPreview && !_gifPreview->currentDisplayed()) {
  147. update();
  148. }
  149. } break;
  150. }
  151. }
  152. } // namespace Ui