scene_item_canvas.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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/scene/scene_item_canvas.h"
  8. #include <QGraphicsScene>
  9. #include <QGraphicsSceneMouseEvent>
  10. namespace Editor {
  11. namespace {
  12. QRectF NormalizedRect(const QPointF& p1, const QPointF& p2) {
  13. return QRectF(
  14. std::min(p1.x(), p2.x()),
  15. std::min(p1.y(), p2.y()),
  16. std::abs(p2.x() - p1.x()) + 1,
  17. std::abs(p2.y() - p1.y()) + 1);
  18. }
  19. std::vector<QPointF> InterpolatedPoints(
  20. const QPointF &startPoint,
  21. const QPointF &endPoint) {
  22. std::vector<QPointF> points;
  23. const auto x1 = startPoint.x();
  24. const auto y1 = startPoint.y();
  25. const auto x2 = endPoint.x();
  26. const auto y2 = endPoint.y();
  27. // Difference of x and y values.
  28. const auto dx = x2 - x1;
  29. const auto dy = y2 - y1;
  30. // Absolute values of differences.
  31. const auto ix = std::abs(dx);
  32. const auto iy = std::abs(dy);
  33. // Larger of the x and y differences.
  34. const auto inc = ix > iy ? ix : iy;
  35. // Plot location.
  36. auto plotx = x1;
  37. auto ploty = y1;
  38. auto x = 0;
  39. auto y = 0;
  40. points.push_back(QPointF(plotx, ploty));
  41. for (auto i = 0; i <= inc; i++) {
  42. const auto xInc = x > inc;
  43. const auto yInc = y > inc;
  44. x += ix;
  45. y += iy;
  46. if (xInc) {
  47. x -= inc;
  48. plotx += 1 * ((dx < 0) ? -1 : 1);
  49. }
  50. if (yInc) {
  51. y -= inc;
  52. ploty += 1 * ((dy < 0) ? -1 : 1);
  53. }
  54. if (xInc || yInc) {
  55. points.push_back(QPointF(plotx, ploty));
  56. }
  57. }
  58. return points;
  59. }
  60. } // namespace
  61. ItemCanvas::ItemCanvas() {
  62. setAcceptedMouseButtons({});
  63. }
  64. void ItemCanvas::clearPixmap() {
  65. _hq = nullptr;
  66. _p = nullptr;
  67. _pixmap = QPixmap(
  68. (scene()->sceneRect().size() * style::DevicePixelRatio()).toSize());
  69. _pixmap.setDevicePixelRatio(style::DevicePixelRatio());
  70. _pixmap.fill(Qt::transparent);
  71. _p = std::make_unique<Painter>(&_pixmap);
  72. _hq = std::make_unique<PainterHighQualityEnabler>(*_p);
  73. _p->setPen(Qt::NoPen);
  74. _p->setBrush(_brushData.color);
  75. }
  76. void ItemCanvas::applyBrush(const QColor &color, float size) {
  77. _brushData.color = color;
  78. _brushData.size = size;
  79. _p->setBrush(color);
  80. _brushMargins = QMarginsF(size, size, size, size);// / 2.;
  81. }
  82. QRectF ItemCanvas::boundingRect() const {
  83. return scene()->sceneRect();
  84. }
  85. void ItemCanvas::computeContentRect(const QPointF &p) {
  86. if (!scene()) {
  87. return;
  88. }
  89. const auto sceneSize = scene()->sceneRect().size();
  90. _contentRect = QRectF(
  91. QPointF(
  92. std::clamp(p.x() - _brushMargins.left(), 0., _contentRect.x()),
  93. std::clamp(p.y() - _brushMargins.top(), 0., _contentRect.y())
  94. ),
  95. QPointF(
  96. std::clamp(
  97. p.x() + _brushMargins.right(),
  98. _contentRect.x() + _contentRect.width(),
  99. sceneSize.width()),
  100. std::clamp(
  101. p.y() + _brushMargins.bottom(),
  102. _contentRect.y() + _contentRect.height(),
  103. sceneSize.height())
  104. ));
  105. }
  106. void ItemCanvas::drawLine(
  107. const QPointF &currentPoint,
  108. const QPointF &lastPoint) {
  109. const auto halfBrushSize = _brushData.size / 2.;
  110. const auto points = InterpolatedPoints(lastPoint, currentPoint);
  111. _rectToUpdate |= NormalizedRect(currentPoint, lastPoint) + _brushMargins;
  112. for (const auto &point : points) {
  113. _p->drawEllipse(point, halfBrushSize, halfBrushSize);
  114. }
  115. }
  116. void ItemCanvas::handleMousePressEvent(
  117. not_null<QGraphicsSceneMouseEvent*> e) {
  118. _lastPoint = e->scenePos();
  119. _contentRect = QRectF(_lastPoint, _lastPoint);
  120. _drawing = true;
  121. }
  122. void ItemCanvas::handleMouseMoveEvent(
  123. not_null<QGraphicsSceneMouseEvent*> e) {
  124. if (!_drawing) {
  125. return;
  126. }
  127. const auto scenePos = e->scenePos();
  128. drawLine(scenePos, _lastPoint);
  129. update(_rectToUpdate);
  130. computeContentRect(scenePos);
  131. _lastPoint = scenePos;
  132. }
  133. void ItemCanvas::handleMouseReleaseEvent(
  134. not_null<QGraphicsSceneMouseEvent*> e) {
  135. if (!_drawing) {
  136. return;
  137. }
  138. _drawing = false;
  139. if (_contentRect.isValid()) {
  140. const auto scaledContentRect = QRectF(
  141. _contentRect.x() * style::DevicePixelRatio(),
  142. _contentRect.y() * style::DevicePixelRatio(),
  143. _contentRect.width() * style::DevicePixelRatio(),
  144. _contentRect.height() * style::DevicePixelRatio());
  145. _grabContentRequests.fire({
  146. .pixmap = _pixmap.copy(scaledContentRect.toRect()),
  147. .position = _contentRect.topLeft(),
  148. });
  149. }
  150. clearPixmap();
  151. update();
  152. }
  153. void ItemCanvas::paint(
  154. QPainter *p,
  155. const QStyleOptionGraphicsItem *,
  156. QWidget *) {
  157. p->fillRect(_rectToUpdate, Qt::transparent);
  158. p->drawPixmap(0, 0, _pixmap);
  159. _rectToUpdate = QRectF();
  160. }
  161. rpl::producer<ItemCanvas::Content> ItemCanvas::grabContentRequests() const {
  162. return _grabContentRequests.events();
  163. }
  164. bool ItemCanvas::collidesWithItem(
  165. const QGraphicsItem *,
  166. Qt::ItemSelectionMode) const {
  167. return false;
  168. }
  169. bool ItemCanvas::collidesWithPath(
  170. const QPainterPath &,
  171. Qt::ItemSelectionMode) const {
  172. return false;
  173. }
  174. void ItemCanvas::cancelDrawing() {
  175. _drawing = false;
  176. _contentRect = QRectF();
  177. clearPixmap();
  178. update();
  179. }
  180. ItemCanvas::~ItemCanvas() {
  181. _hq = nullptr;
  182. _p = nullptr;
  183. }
  184. } // namespace Editor