data_photo_media.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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 "data/data_photo_media.h"
  8. #include "data/data_file_origin.h"
  9. #include "data/data_session.h"
  10. #include "history/history.h"
  11. #include "history/history_item.h"
  12. #include "main/main_session.h"
  13. #include "main/main_session_settings.h"
  14. #include "storage/file_download.h"
  15. #include <QtGui/QGuiApplication>
  16. #include <QtGui/QClipboard>
  17. namespace Data {
  18. PhotoMedia::PhotoMedia(not_null<PhotoData*> owner)
  19. : _owner(owner) {
  20. }
  21. // NB! Right now DocumentMedia can outlive Main::Session!
  22. // In DocumentData::collectLocalData a shared_ptr is sent on_main.
  23. // In case this is a problem the ~Gif code should be rewritten.
  24. PhotoMedia::~PhotoMedia() = default;
  25. not_null<PhotoData*> PhotoMedia::owner() const {
  26. return _owner;
  27. }
  28. Image *PhotoMedia::thumbnailInline() const {
  29. if (!_inlineThumbnail) {
  30. const auto bytes = _owner->inlineThumbnailBytes();
  31. if (!bytes.isEmpty()) {
  32. auto image = Images::FromInlineBytes(bytes);
  33. if (image.isNull()) {
  34. _owner->clearInlineThumbnailBytes();
  35. } else {
  36. _inlineThumbnail = std::make_unique<Image>(std::move(image));
  37. }
  38. }
  39. }
  40. return _inlineThumbnail.get();
  41. }
  42. Image *PhotoMedia::image(PhotoSize size) const {
  43. if (const auto resolved = resolveLoadedImage(size)) {
  44. return resolved->data.get();
  45. }
  46. return nullptr;
  47. }
  48. QByteArray PhotoMedia::imageBytes(PhotoSize size) const {
  49. if (const auto resolved = resolveLoadedImage(size)) {
  50. return resolved->bytes;
  51. }
  52. return QByteArray();
  53. }
  54. auto PhotoMedia::resolveLoadedImage(PhotoSize size) const
  55. -> const PhotoImage * {
  56. const auto &original = _images[PhotoSizeIndex(size)];
  57. if (const auto image = original.data.get()) {
  58. if (original.goodFor >= size) {
  59. return &original;
  60. }
  61. }
  62. const auto &valid = _images[_owner->validSizeIndex(size)];
  63. if (const auto image = valid.data.get()) {
  64. if (valid.goodFor >= size) {
  65. return &valid;
  66. }
  67. }
  68. return nullptr;
  69. }
  70. void PhotoMedia::wanted(PhotoSize size, Data::FileOrigin origin) {
  71. const auto index = _owner->validSizeIndex(size);
  72. if (!_images[index].data || _images[index].goodFor < size) {
  73. _owner->load(size, origin);
  74. }
  75. }
  76. QSize PhotoMedia::size(PhotoSize size) const {
  77. const auto index = PhotoSizeIndex(size);
  78. if (const auto image = _images[index].data.get()) {
  79. return image->size();
  80. }
  81. const auto &location = _owner->location(size);
  82. return { location.width(), location.height() };
  83. }
  84. void PhotoMedia::set(
  85. PhotoSize size,
  86. PhotoSize goodFor,
  87. QImage image,
  88. QByteArray bytes) {
  89. const auto index = PhotoSizeIndex(size);
  90. const auto limit = PhotoData::SideLimit();
  91. if (image.width() > limit || image.height() > limit) {
  92. image = image.scaled(
  93. limit,
  94. limit,
  95. Qt::KeepAspectRatio,
  96. Qt::SmoothTransformation);
  97. }
  98. _images[index] = PhotoImage{
  99. .data = std::make_unique<Image>(std::move(image)),
  100. .bytes = std::move(bytes),
  101. .goodFor = goodFor,
  102. };
  103. _owner->session().notifyDownloaderTaskFinished();
  104. }
  105. QByteArray PhotoMedia::videoContent(PhotoSize size) const {
  106. const auto small = (size == PhotoSize::Small) && _owner->hasVideoSmall();
  107. return small ? _videoBytesSmall : _videoBytesLarge;
  108. }
  109. QSize PhotoMedia::videoSize(PhotoSize size) const {
  110. const auto &location = _owner->videoLocation(size);
  111. return { location.width(), location.height() };
  112. }
  113. void PhotoMedia::videoWanted(PhotoSize size, Data::FileOrigin origin) {
  114. if (videoContent(size).isEmpty()) {
  115. _owner->loadVideo(size, origin);
  116. }
  117. }
  118. void PhotoMedia::setVideo(PhotoSize size, QByteArray content) {
  119. const auto small = (size == PhotoSize::Small) && _owner->hasVideoSmall();
  120. (small ? _videoBytesSmall : _videoBytesLarge) = std::move(content);
  121. }
  122. bool PhotoMedia::loaded() const {
  123. const auto index = PhotoSizeIndex(PhotoSize::Large);
  124. return (_images[index].data != nullptr)
  125. && (_images[index].goodFor >= PhotoSize::Large);
  126. }
  127. float64 PhotoMedia::progress() const {
  128. return (_owner->uploading() || _owner->loading())
  129. ? _owner->progress()
  130. : (loaded() ? 1. : 0.);
  131. }
  132. bool PhotoMedia::autoLoadThumbnailAllowed(not_null<PeerData*> peer) const {
  133. if (loaded() || _owner->cancelled()) {
  134. return false;
  135. }
  136. return _owner->hasExact(PhotoSize::Small)
  137. || _owner->hasExact(PhotoSize::Thumbnail)
  138. || AutoDownload::Should(
  139. _owner->session().settings().autoDownload(),
  140. peer,
  141. _owner);
  142. }
  143. void PhotoMedia::automaticLoad(
  144. FileOrigin origin,
  145. const HistoryItem *item) {
  146. if (item) {
  147. automaticLoad(origin, item->history()->peer);
  148. }
  149. }
  150. void PhotoMedia::automaticLoad(
  151. FileOrigin origin,
  152. not_null<PeerData*> peer) {
  153. if (loaded() || _owner->cancelled()) {
  154. return;
  155. }
  156. const auto loadFromCloud = Data::AutoDownload::Should(
  157. _owner->session().settings().autoDownload(),
  158. peer,
  159. _owner);
  160. _owner->load(
  161. origin,
  162. loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly,
  163. true);
  164. }
  165. void PhotoMedia::collectLocalData(not_null<PhotoMedia*> local) {
  166. if (const auto image = local->_inlineThumbnail.get()) {
  167. _inlineThumbnail = std::make_unique<Image>(image->original());
  168. }
  169. for (auto i = 0; i != kPhotoSizeCount; ++i) {
  170. if (const auto image = local->_images[i].data.get()) {
  171. _images[i] = PhotoImage{
  172. .data = std::make_unique<Image>(image->original()),
  173. .goodFor = local->_images[i].goodFor
  174. };
  175. }
  176. }
  177. }
  178. bool PhotoMedia::saveToFile(const QString &path) {
  179. constexpr auto large = PhotoSize::Large;
  180. if (const auto video = videoContent(large); !video.isEmpty()) {
  181. QFile f(path);
  182. return f.open(QIODevice::WriteOnly)
  183. && (f.write(video) == video.size());
  184. } else if (const auto photo = imageBytes(large); !photo.isEmpty()) {
  185. QFile f(path);
  186. return f.open(QIODevice::WriteOnly)
  187. && (f.write(photo) == photo.size());
  188. } else if (const auto fallback = image(large)->original()
  189. ; !fallback.isNull()) {
  190. return fallback.save(path, "JPG");
  191. }
  192. return false;
  193. }
  194. bool PhotoMedia::setToClipboard() {
  195. constexpr auto large = PhotoSize::Large;
  196. if (const auto video = videoContent(large); !video.isEmpty()) {
  197. return false;
  198. }
  199. auto fallback = image(large)->original();
  200. if (fallback.isNull()) {
  201. return false;
  202. }
  203. auto mime = std::make_unique<QMimeData>();
  204. mime->setImageData(std::move(fallback));
  205. if (auto bytes = imageBytes(large); !bytes.isEmpty()) {
  206. mime->setData(u"image/jpeg"_q, std::move(bytes));
  207. }
  208. mime->setData(u"application/x-td-use-jpeg"_q, "1");
  209. QGuiApplication::clipboard()->setMimeData(mime.release());
  210. return true;
  211. }
  212. } // namespace Data