photo_editor.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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/photo_editor.h"
  8. #include "core/application.h"
  9. #include "core/core_settings.h"
  10. #include "editor/color_picker.h"
  11. #include "editor/controllers/controllers.h"
  12. #include "editor/photo_editor_content.h"
  13. #include "editor/photo_editor_controls.h"
  14. #include "window/window_controller.h"
  15. #include "window/window_session_controller.h"
  16. #include "ui/layers/layer_widget.h"
  17. #include "styles/style_editor.h"
  18. namespace Editor {
  19. namespace {
  20. constexpr auto kPrecision = 100000;
  21. [[nodiscard]] QByteArray Serialize(const Brush &brush) {
  22. auto result = QByteArray();
  23. auto stream = QDataStream(&result, QIODevice::WriteOnly);
  24. stream.setVersion(QDataStream::Qt_5_3);
  25. stream << qint32(brush.sizeRatio * kPrecision) << brush.color;
  26. stream.device()->close();
  27. return result;
  28. }
  29. [[nodiscard]] Brush Deserialize(const QByteArray &data) {
  30. auto stream = QDataStream(data);
  31. auto result = Brush();
  32. auto size = qint32(0);
  33. stream >> size >> result.color;
  34. result.sizeRatio = size / float(kPrecision);
  35. return (stream.status() != QDataStream::Ok)
  36. ? Brush()
  37. : result;
  38. }
  39. } // namespace
  40. PhotoEditor::PhotoEditor(
  41. not_null<QWidget*> parent,
  42. not_null<Window::Controller*> controller,
  43. std::shared_ptr<Image> photo,
  44. PhotoModifications modifications,
  45. EditorData data)
  46. : PhotoEditor(
  47. parent,
  48. controller->uiShow(),
  49. (controller->sessionController()
  50. ? controller->sessionController()->uiShow()
  51. : nullptr),
  52. std::move(photo),
  53. std::move(modifications),
  54. std::move(data)) {
  55. }
  56. PhotoEditor::PhotoEditor(
  57. not_null<QWidget*> parent,
  58. std::shared_ptr<Ui::Show> show,
  59. std::shared_ptr<ChatHelpers::Show> sessionShow,
  60. std::shared_ptr<Image> photo,
  61. PhotoModifications modifications,
  62. EditorData data)
  63. : RpWidget(parent)
  64. , _modifications(std::move(modifications))
  65. , _controllers(std::make_shared<Controllers>(
  66. sessionShow
  67. ? std::make_unique<StickersPanelController>(
  68. this,
  69. std::move(sessionShow))
  70. : nullptr,
  71. std::make_unique<UndoController>(),
  72. std::move(show)))
  73. , _content(base::make_unique_q<PhotoEditorContent>(
  74. this,
  75. photo,
  76. _modifications,
  77. _controllers,
  78. data))
  79. , _controls(base::make_unique_q<PhotoEditorControls>(
  80. this,
  81. _controllers,
  82. _modifications,
  83. data))
  84. , _colorPicker(std::make_unique<ColorPicker>(
  85. this,
  86. Deserialize(Core::App().settings().photoEditorBrush()))) {
  87. sizeValue(
  88. ) | rpl::start_with_next([=](const QSize &size) {
  89. if (size.isEmpty()) {
  90. return;
  91. }
  92. _content->setGeometry(rect() - st::photoEditorContentMargins);
  93. }, lifetime());
  94. _content->innerRect(
  95. ) | rpl::start_with_next([=](QRect inner) {
  96. if (inner.isEmpty()) {
  97. return;
  98. }
  99. const auto innerTop = _content->y() + inner.top();
  100. const auto skip = st::photoEditorCropPointSize;
  101. const auto controlsRect = rect()
  102. - style::margins(0, innerTop + inner.height() + skip, 0, 0);
  103. _controls->setGeometry(controlsRect);
  104. }, lifetime());
  105. _controls->colorLinePositionValue(
  106. ) | rpl::start_with_next([=](const QPoint &p) {
  107. _colorPicker->moveLine(p);
  108. }, _controls->lifetime());
  109. _controls->colorLineShownValue(
  110. ) | rpl::start_with_next([=](bool shown) {
  111. _colorPicker->setVisible(shown);
  112. }, _controls->lifetime());
  113. _mode.value(
  114. ) | rpl::start_with_next([=](const PhotoEditorMode &mode) {
  115. _content->applyMode(mode);
  116. _controls->applyMode(mode);
  117. }, lifetime());
  118. _controls->rotateRequests(
  119. ) | rpl::start_with_next([=](int angle) {
  120. _modifications.angle += 90;
  121. if (_modifications.angle >= 360) {
  122. _modifications.angle -= 360;
  123. }
  124. _content->applyModifications(_modifications);
  125. }, lifetime());
  126. _controls->flipRequests(
  127. ) | rpl::start_with_next([=] {
  128. _modifications.flipped = !_modifications.flipped;
  129. _content->applyModifications(_modifications);
  130. }, lifetime());
  131. _controls->paintModeRequests(
  132. ) | rpl::start_with_next([=] {
  133. _mode = PhotoEditorMode{
  134. .mode = PhotoEditorMode::Mode::Paint,
  135. .action = PhotoEditorMode::Action::None,
  136. };
  137. }, lifetime());
  138. _controls->doneRequests(
  139. ) | rpl::start_with_next([=] {
  140. const auto mode = _mode.current().mode;
  141. if (mode == PhotoEditorMode::Mode::Paint) {
  142. _mode = PhotoEditorMode{
  143. .mode = PhotoEditorMode::Mode::Transform,
  144. .action = PhotoEditorMode::Action::Save,
  145. };
  146. } else if (mode == PhotoEditorMode::Mode::Transform) {
  147. _mode = PhotoEditorMode{
  148. .mode = PhotoEditorMode::Mode::Out,
  149. .action = PhotoEditorMode::Action::Save,
  150. };
  151. save();
  152. }
  153. }, lifetime());
  154. _controls->cancelRequests(
  155. ) | rpl::start_with_next([=] {
  156. const auto mode = _mode.current().mode;
  157. if (mode == PhotoEditorMode::Mode::Paint) {
  158. _mode = PhotoEditorMode{
  159. .mode = PhotoEditorMode::Mode::Transform,
  160. .action = PhotoEditorMode::Action::Discard,
  161. };
  162. } else if (mode == PhotoEditorMode::Mode::Transform) {
  163. _mode = PhotoEditorMode{
  164. .mode = PhotoEditorMode::Mode::Out,
  165. .action = PhotoEditorMode::Action::Discard,
  166. };
  167. _cancel.fire({});
  168. }
  169. }, lifetime());
  170. _colorPicker->saveBrushRequests(
  171. ) | rpl::start_with_next([=](const Brush &brush) {
  172. _content->applyBrush(brush);
  173. const auto serialized = Serialize(brush);
  174. if (Core::App().settings().photoEditorBrush() != serialized) {
  175. Core::App().settings().setPhotoEditorBrush(serialized);
  176. Core::App().saveSettingsDelayed();
  177. }
  178. }, lifetime());
  179. }
  180. void PhotoEditor::keyPressEvent(QKeyEvent *e) {
  181. if (!_colorPicker->preventHandleKeyPress()) {
  182. _content->handleKeyPress(e) || _controls->handleKeyPress(e);
  183. }
  184. }
  185. void PhotoEditor::save() {
  186. _content->save(_modifications);
  187. _done.fire_copy(_modifications);
  188. }
  189. rpl::producer<PhotoModifications> PhotoEditor::doneRequests() const {
  190. return _done.events();
  191. }
  192. rpl::producer<> PhotoEditor::cancelRequests() const {
  193. return _cancel.events();
  194. }
  195. void InitEditorLayer(
  196. not_null<Ui::LayerWidget*> layer,
  197. not_null<PhotoEditor*> editor,
  198. Fn<void(PhotoModifications)> doneCallback) {
  199. editor->cancelRequests(
  200. ) | rpl::start_with_next([=] {
  201. layer->closeLayer();
  202. }, editor->lifetime());
  203. const auto weak = Ui::MakeWeak(layer.get());
  204. editor->doneRequests(
  205. ) | rpl::start_with_next([=, done = std::move(doneCallback)](
  206. const PhotoModifications &mods) {
  207. done(mods);
  208. if (const auto strong = weak.data()) {
  209. strong->closeLayer();
  210. }
  211. }, editor->lifetime());
  212. }
  213. } // namespace Editor