image.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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/image/image.h"
  8. #include "storage/cache/storage_cache_database.h"
  9. #include "data/data_session.h"
  10. #include "main/main_session.h"
  11. #include "ui/ui_utility.h"
  12. using namespace Images;
  13. namespace Images {
  14. namespace {
  15. [[nodiscard]] uint64 PixKey(int width, int height, Options options) {
  16. return static_cast<uint64>(width)
  17. | (static_cast<uint64>(height) << 24)
  18. | (static_cast<uint64>(options) << 48);
  19. }
  20. [[nodiscard]] uint64 SinglePixKey(Options options) {
  21. return PixKey(0, 0, options);
  22. }
  23. [[nodiscard]] Options OptionsByArgs(const PrepareArgs &args) {
  24. return args.options | (args.colored ? Option::Colorize : Option::None);
  25. }
  26. [[nodiscard]] uint64 PixKey(int width, int height, const PrepareArgs &args) {
  27. return PixKey(width, height, OptionsByArgs(args));
  28. }
  29. [[nodiscard]] uint64 SinglePixKey(const PrepareArgs &args) {
  30. return SinglePixKey(OptionsByArgs(args));
  31. }
  32. } // namespace
  33. } // namespace Images
  34. Image::Image(const QString &path)
  35. : Image(Read({ .path = path }).image) {
  36. }
  37. Image::Image(const QByteArray &content)
  38. : Image(Read({ .content = content }).image) {
  39. }
  40. Image::Image(QImage &&data)
  41. : _data(data.isNull() ? Empty()->original() : std::move(data)) {
  42. Expects(!_data.isNull());
  43. }
  44. not_null<Image*> Image::Empty() {
  45. static auto result = Image([] {
  46. const auto factor = style::DevicePixelRatio();
  47. auto data = QImage(
  48. factor,
  49. factor,
  50. QImage::Format_ARGB32_Premultiplied);
  51. data.fill(Qt::transparent);
  52. data.setDevicePixelRatio(style::DevicePixelRatio());
  53. return data;
  54. }());
  55. return &result;
  56. }
  57. not_null<Image*> Image::BlankMedia() {
  58. static auto result = Image([] {
  59. const auto factor = style::DevicePixelRatio();
  60. auto data = QImage(
  61. factor,
  62. factor,
  63. QImage::Format_ARGB32_Premultiplied);
  64. data.fill(Qt::black);
  65. data.setDevicePixelRatio(style::DevicePixelRatio());
  66. return data;
  67. }());
  68. return &result;
  69. }
  70. QImage Image::original() const {
  71. return _data;
  72. }
  73. const QPixmap &Image::cached(
  74. int w,
  75. int h,
  76. const Images::PrepareArgs &args,
  77. bool single) const {
  78. const auto ratio = style::DevicePixelRatio();
  79. if (w <= 0 || !width() || !height()) {
  80. w = width();
  81. } else if (h <= 0) {
  82. h = std::max(int(int64(height()) * w / width()), 1) * ratio;
  83. w *= ratio;
  84. } else {
  85. w *= ratio;
  86. h *= ratio;
  87. }
  88. const auto outer = args.outer;
  89. const auto size = outer.isEmpty() ? QSize(w, h) : outer * ratio;
  90. const auto k = single ? SinglePixKey(args) : PixKey(w, h, args);
  91. const auto i = _cache.find(k);
  92. return (i != _cache.cend() && i->second.size() == size)
  93. ? i->second
  94. : _cache.emplace_or_assign(k, prepare(w, h, args)).first->second;
  95. }
  96. QPixmap Image::prepare(int w, int h, const Images::PrepareArgs &args) const {
  97. if (_data.isNull()) {
  98. if (h <= 0 && height() > 0) {
  99. h = qRound(width() * w / float64(height()));
  100. }
  101. return Empty()->prepare(w, h, args);
  102. }
  103. auto outer = args.outer;
  104. if (!isNull() || outer.isEmpty()) {
  105. return Ui::PixmapFromImage(Prepare(_data, w, h, args));
  106. }
  107. const auto ratio = style::DevicePixelRatio();
  108. const auto outerw = outer.width() * ratio;
  109. const auto outerh = outer.height() * ratio;
  110. auto result = QImage(
  111. QSize(outerw, outerh),
  112. QImage::Format_ARGB32_Premultiplied);
  113. result.setDevicePixelRatio(ratio);
  114. auto p = QPainter(&result);
  115. if (w < outerw) {
  116. p.fillRect(0, 0, (outerw - w) / 2, result.height(), Qt::black);
  117. p.fillRect(((outerw - w) / 2) + w, 0, result.width() - (((outerw - w) / 2) + w), result.height(), Qt::black);
  118. }
  119. if (h < outerh) {
  120. p.fillRect(qMax(0, (outerw - w) / 2), 0, qMin(result.width(), w), (outerh - h) / 2, Qt::black);
  121. p.fillRect(qMax(0, (outerw - w) / 2), ((outerh - h) / 2) + h, qMin(result.width(), w), result.height() - (((outerh - h) / 2) + h), Qt::black);
  122. }
  123. p.fillRect(qMax(0, (outerw - w) / 2), qMax(0, (outerh - h) / 2), qMin(result.width(), w), qMin(result.height(), h), Qt::white);
  124. p.end();
  125. result = Round(std::move(result), args.options);
  126. if (args.colored) {
  127. result = Colored(std::move(result), *args.colored);
  128. }
  129. return Ui::PixmapFromImage(std::move(result));
  130. }