editor_paint.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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 "editor/editor_paint.h"
  8. #include "core/mime_type.h"
  9. #include "ui/boxes/confirm_box.h"
  10. #include "editor/controllers/controllers.h"
  11. #include "editor/scene/scene.h"
  12. #include "editor/scene/scene_item_canvas.h"
  13. #include "editor/scene/scene_item_image.h"
  14. #include "editor/scene/scene_item_sticker.h"
  15. #include "lang/lang_keys.h"
  16. #include "lottie/lottie_single_player.h"
  17. #include "storage/storage_media_prepare.h"
  18. #include "ui/chat/attach/attach_prepare.h"
  19. #include "ui/ui_utility.h"
  20. #include <QGraphicsView>
  21. #include <QtCore/QMimeData>
  22. namespace Editor {
  23. namespace {
  24. constexpr auto kMaxBrush = 25.;
  25. constexpr auto kMinBrush = 1.;
  26. std::shared_ptr<Scene> EnsureScene(
  27. PhotoModifications &mods,
  28. const QSize &size) {
  29. if (!mods.paint) {
  30. mods.paint = std::make_shared<Scene>(QRectF(QPointF(), size));
  31. }
  32. return mods.paint;
  33. }
  34. } // namespace
  35. using ItemPtr = std::shared_ptr<QGraphicsItem>;
  36. Paint::Paint(
  37. not_null<Ui::RpWidget*> parent,
  38. PhotoModifications &modifications,
  39. const QSize &imageSize,
  40. std::shared_ptr<Controllers> controllers)
  41. : RpWidget(parent)
  42. , _controllers(controllers)
  43. , _scene(EnsureScene(modifications, imageSize))
  44. , _view(base::make_unique_q<QGraphicsView>(_scene.get(), this))
  45. , _imageSize(imageSize) {
  46. Expects(modifications.paint != nullptr);
  47. keepResult();
  48. _view->show();
  49. _view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  50. _view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  51. _view->setFrameStyle(int(QFrame::NoFrame) | QFrame::Plain);
  52. _view->viewport()->setAutoFillBackground(false);
  53. // Undo / Redo.
  54. controllers->undoController->performRequestChanges(
  55. ) | rpl::start_with_next([=](const Undo &command) {
  56. if (command == Undo::Undo) {
  57. _scene->performUndo();
  58. } else {
  59. _scene->performRedo();
  60. }
  61. _hasUndo = _scene->hasUndo();
  62. _hasRedo = _scene->hasRedo();
  63. }, lifetime());
  64. controllers->undoController->setCanPerformChanges(rpl::merge(
  65. _hasUndo.value() | rpl::map([](bool enable) {
  66. return UndoController::EnableRequest{
  67. .command = Undo::Undo,
  68. .enable = enable,
  69. };
  70. }),
  71. _hasRedo.value() | rpl::map([](bool enable) {
  72. return UndoController::EnableRequest{
  73. .command = Undo::Redo,
  74. .enable = enable,
  75. };
  76. })));
  77. if (controllers->stickersPanelController) {
  78. using ShowRequest = StickersPanelController::ShowRequest;
  79. controllers->stickersPanelController->setShowRequestChanges(
  80. controllers->stickersPanelController->stickerChosen(
  81. ) | rpl::map_to(ShowRequest::HideAnimated));
  82. controllers->stickersPanelController->stickerChosen(
  83. ) | rpl::start_with_next([=](not_null<DocumentData*> document) {
  84. const auto item = std::make_shared<ItemSticker>(
  85. document,
  86. itemBaseData());
  87. _scene->addItem(item);
  88. _scene->clearSelection();
  89. }, lifetime());
  90. }
  91. rpl::merge(
  92. controllers->stickersPanelController
  93. ? controllers->stickersPanelController->stickerChosen(
  94. ) | rpl::to_empty
  95. : rpl::never<>() | rpl::type_erased(),
  96. _scene->addsItem()
  97. ) | rpl::start_with_next([=] {
  98. clearRedoList();
  99. updateUndoState();
  100. }, lifetime());
  101. _scene->removesItem(
  102. ) | rpl::start_with_next([=] {
  103. updateUndoState();
  104. }, lifetime());
  105. }
  106. void Paint::applyTransform(QRect geometry, int angle, bool flipped) {
  107. if (geometry.isEmpty()) {
  108. return;
  109. }
  110. setGeometry(geometry);
  111. const auto size = geometry.size();
  112. const auto rotatedImageSize = QTransform()
  113. .rotate(angle)
  114. .mapRect(QRect(QPoint(), _imageSize));
  115. const auto ratioW = size.width() / float64(rotatedImageSize.width())
  116. * (flipped ? -1 : 1);
  117. const auto ratioH = size.height() / float64(rotatedImageSize.height());
  118. _view->setTransform(QTransform().scale(ratioW, ratioH).rotate(angle));
  119. _view->setGeometry(QRect(QPoint(), size));
  120. _transform = {
  121. .angle = angle,
  122. .flipped = flipped,
  123. .zoom = size.width() / float64(_scene->sceneRect().width()),
  124. };
  125. _scene->updateZoom(_transform.zoom);
  126. }
  127. std::shared_ptr<Scene> Paint::saveScene() const {
  128. _scene->save(SaveState::Save);
  129. return _scene->items().empty()
  130. ? nullptr
  131. : _scene;
  132. }
  133. void Paint::restoreScene() {
  134. _scene->restore(SaveState::Save);
  135. }
  136. void Paint::cancel() {
  137. _scene->restore(SaveState::Keep);
  138. }
  139. void Paint::keepResult() {
  140. _scene->save(SaveState::Keep);
  141. }
  142. void Paint::clearRedoList() {
  143. _scene->clearRedoList();
  144. _hasRedo = false;
  145. }
  146. void Paint::updateUndoState() {
  147. _hasUndo = _scene->hasUndo();
  148. _hasRedo = _scene->hasRedo();
  149. }
  150. void Paint::applyBrush(const Brush &brush) {
  151. _scene->applyBrush(
  152. brush.color,
  153. (kMinBrush + float64(kMaxBrush - kMinBrush) * brush.sizeRatio));
  154. }
  155. void Paint::handleMimeData(const QMimeData *data) {
  156. const auto add = [&](QImage image) {
  157. if (image.isNull()) {
  158. return;
  159. }
  160. if (!Ui::ValidateThumbDimensions(image.width(), image.height())) {
  161. _controllers->show->showBox(
  162. Ui::MakeInformBox(tr::lng_edit_media_invalid_file()));
  163. return;
  164. }
  165. const auto item = std::make_shared<ItemImage>(
  166. Ui::PixmapFromImage(std::move(image)),
  167. itemBaseData());
  168. _scene->addItem(item);
  169. _scene->clearSelection();
  170. };
  171. using Error = Ui::PreparedList::Error;
  172. const auto premium = false; // Don't support > 2GB files here.
  173. const auto list = Core::ReadMimeUrls(data);
  174. auto result = !list.isEmpty()
  175. ? Storage::PrepareMediaList(
  176. list.mid(0, 1),
  177. _imageSize.width() / 2,
  178. premium)
  179. : Ui::PreparedList(Error::EmptyFile, QString());
  180. if (result.error == Error::None) {
  181. add(base::take(result.files.front().preview));
  182. } else if (auto read = Core::ReadMimeImage(data)) {
  183. add(std::move(read.image));
  184. }
  185. }
  186. ItemBase::Data Paint::itemBaseData() const {
  187. const auto s = _scene->sceneRect().toRect().size();
  188. const auto size = std::min(s.width(), s.height()) / 2;
  189. const auto x = s.width() / 2;
  190. const auto y = s.height() / 2;
  191. return ItemBase::Data{
  192. .initialZoom = _transform.zoom,
  193. .zPtr = _scene->lastZ(),
  194. .size = size,
  195. .x = x,
  196. .y = y,
  197. .flipped = _transform.flipped,
  198. .rotation = -_transform.angle,
  199. .imageSize = _imageSize,
  200. };
  201. }
  202. } // namespace Editor