224 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			224 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
This file is part of Telegram Desktop,
 | 
						|
the official desktop application for the Telegram messaging service.
 | 
						|
 | 
						|
For license and copyright information please follow this link:
 | 
						|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
						|
*/
 | 
						|
#include "editor/scene/scene_item_canvas.h"
 | 
						|
 | 
						|
#include <QGraphicsScene>
 | 
						|
#include <QGraphicsSceneMouseEvent>
 | 
						|
 | 
						|
namespace Editor {
 | 
						|
namespace {
 | 
						|
 | 
						|
QRectF NormalizedRect(const QPointF& p1, const QPointF& p2) {
 | 
						|
	return QRectF(
 | 
						|
		std::min(p1.x(), p2.x()),
 | 
						|
		std::min(p1.y(), p2.y()),
 | 
						|
		std::abs(p2.x() - p1.x()) + 1,
 | 
						|
		std::abs(p2.y() - p1.y()) + 1);
 | 
						|
}
 | 
						|
 | 
						|
std::vector<QPointF> InterpolatedPoints(
 | 
						|
		const QPointF &startPoint,
 | 
						|
		const QPointF &endPoint) {
 | 
						|
	std::vector<QPointF> points;
 | 
						|
 | 
						|
	const auto x1 = startPoint.x();
 | 
						|
	const auto y1 = startPoint.y();
 | 
						|
	const auto x2 = endPoint.x();
 | 
						|
	const auto y2 = endPoint.y();
 | 
						|
 | 
						|
	// Difference of x and y values.
 | 
						|
	const auto dx = x2 - x1;
 | 
						|
	const auto dy = y2 - y1;
 | 
						|
 | 
						|
	// Absolute values of differences.
 | 
						|
	const auto ix = std::abs(dx);
 | 
						|
	const auto iy = std::abs(dy);
 | 
						|
 | 
						|
	// Larger of the x and y differences.
 | 
						|
	const auto inc = ix > iy ? ix : iy;
 | 
						|
 | 
						|
	// Plot location.
 | 
						|
	auto plotx = x1;
 | 
						|
	auto ploty = y1;
 | 
						|
 | 
						|
	auto x = 0;
 | 
						|
	auto y = 0;
 | 
						|
 | 
						|
	points.push_back(QPointF(plotx, ploty));
 | 
						|
 | 
						|
	for (auto i = 0; i <= inc; i++) {
 | 
						|
		const auto xInc = x > inc;
 | 
						|
		const auto yInc = y > inc;
 | 
						|
 | 
						|
		x += ix;
 | 
						|
		y += iy;
 | 
						|
 | 
						|
		if (xInc) {
 | 
						|
			x -= inc;
 | 
						|
			plotx += 1 * ((dx < 0) ? -1 : 1);
 | 
						|
		}
 | 
						|
 | 
						|
		if (yInc) {
 | 
						|
			y -= inc;
 | 
						|
			ploty += 1 * ((dy < 0) ? -1 : 1);
 | 
						|
		}
 | 
						|
 | 
						|
		if (xInc || yInc) {
 | 
						|
			points.push_back(QPointF(plotx, ploty));
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return points;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
ItemCanvas::ItemCanvas() {
 | 
						|
	setAcceptedMouseButtons({});
 | 
						|
}
 | 
						|
 | 
						|
void ItemCanvas::clearPixmap() {
 | 
						|
	_hq = nullptr;
 | 
						|
	_p = nullptr;
 | 
						|
 | 
						|
	_pixmap = QPixmap(
 | 
						|
		(scene()->sceneRect().size() * style::DevicePixelRatio()).toSize());
 | 
						|
	_pixmap.setDevicePixelRatio(style::DevicePixelRatio());
 | 
						|
	_pixmap.fill(Qt::transparent);
 | 
						|
 | 
						|
	_p = std::make_unique<Painter>(&_pixmap);
 | 
						|
	_hq = std::make_unique<PainterHighQualityEnabler>(*_p);
 | 
						|
	_p->setPen(Qt::NoPen);
 | 
						|
	_p->setBrush(_brushData.color);
 | 
						|
}
 | 
						|
 | 
						|
void ItemCanvas::applyBrush(const QColor &color, float size) {
 | 
						|
	_brushData.color = color;
 | 
						|
	_brushData.size = size;
 | 
						|
	_p->setBrush(color);
 | 
						|
	_brushMargins = QMarginsF(size, size, size, size);// / 2.;
 | 
						|
}
 | 
						|
 | 
						|
QRectF ItemCanvas::boundingRect() const {
 | 
						|
	return scene()->sceneRect();
 | 
						|
}
 | 
						|
 | 
						|
void ItemCanvas::computeContentRect(const QPointF &p) {
 | 
						|
	if (!scene()) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	const auto sceneSize = scene()->sceneRect().size();
 | 
						|
	_contentRect = QRectF(
 | 
						|
		QPointF(
 | 
						|
			std::clamp(p.x() - _brushMargins.left(), 0., _contentRect.x()),
 | 
						|
			std::clamp(p.y() - _brushMargins.top(), 0., _contentRect.y())
 | 
						|
		),
 | 
						|
		QPointF(
 | 
						|
			std::clamp(
 | 
						|
				p.x() + _brushMargins.right(),
 | 
						|
				_contentRect.x() + _contentRect.width(),
 | 
						|
				sceneSize.width()),
 | 
						|
			std::clamp(
 | 
						|
				p.y() + _brushMargins.bottom(),
 | 
						|
				_contentRect.y() + _contentRect.height(),
 | 
						|
				sceneSize.height())
 | 
						|
		));
 | 
						|
}
 | 
						|
 | 
						|
void ItemCanvas::drawLine(
 | 
						|
		const QPointF ¤tPoint,
 | 
						|
		const QPointF &lastPoint) {
 | 
						|
	const auto halfBrushSize = _brushData.size / 2.;
 | 
						|
	const auto points = InterpolatedPoints(lastPoint, currentPoint);
 | 
						|
 | 
						|
	_rectToUpdate |= NormalizedRect(currentPoint, lastPoint) + _brushMargins;
 | 
						|
 | 
						|
	for (const auto &point : points) {
 | 
						|
		_p->drawEllipse(point, halfBrushSize, halfBrushSize);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void ItemCanvas::handleMousePressEvent(
 | 
						|
		not_null<QGraphicsSceneMouseEvent*> e) {
 | 
						|
	_lastPoint = e->scenePos();
 | 
						|
	_contentRect = QRectF(_lastPoint, _lastPoint);
 | 
						|
	_drawing = true;
 | 
						|
}
 | 
						|
 | 
						|
void ItemCanvas::handleMouseMoveEvent(
 | 
						|
		not_null<QGraphicsSceneMouseEvent*> e) {
 | 
						|
	if (!_drawing) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	const auto scenePos = e->scenePos();
 | 
						|
	drawLine(scenePos, _lastPoint);
 | 
						|
	update(_rectToUpdate);
 | 
						|
	computeContentRect(scenePos);
 | 
						|
	_lastPoint = scenePos;
 | 
						|
}
 | 
						|
 | 
						|
void ItemCanvas::handleMouseReleaseEvent(
 | 
						|
		not_null<QGraphicsSceneMouseEvent*> e) {
 | 
						|
	if (!_drawing) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	_drawing = false;
 | 
						|
 | 
						|
	if (_contentRect.isValid()) {
 | 
						|
		const auto scaledContentRect = QRectF(
 | 
						|
			_contentRect.x() * style::DevicePixelRatio(),
 | 
						|
			_contentRect.y() * style::DevicePixelRatio(),
 | 
						|
			_contentRect.width() * style::DevicePixelRatio(),
 | 
						|
			_contentRect.height() * style::DevicePixelRatio());
 | 
						|
 | 
						|
		_grabContentRequests.fire({
 | 
						|
			.pixmap = _pixmap.copy(scaledContentRect.toRect()),
 | 
						|
			.position = _contentRect.topLeft(),
 | 
						|
		});
 | 
						|
	}
 | 
						|
	clearPixmap();
 | 
						|
	update();
 | 
						|
}
 | 
						|
 | 
						|
void ItemCanvas::paint(
 | 
						|
		QPainter *p,
 | 
						|
		const QStyleOptionGraphicsItem *,
 | 
						|
		QWidget *) {
 | 
						|
	p->fillRect(_rectToUpdate, Qt::transparent);
 | 
						|
	p->drawPixmap(0, 0, _pixmap);
 | 
						|
	_rectToUpdate = QRectF();
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<ItemCanvas::Content> ItemCanvas::grabContentRequests() const {
 | 
						|
	return _grabContentRequests.events();
 | 
						|
}
 | 
						|
 | 
						|
bool ItemCanvas::collidesWithItem(
 | 
						|
		const QGraphicsItem *,
 | 
						|
		Qt::ItemSelectionMode) const {
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
bool ItemCanvas::collidesWithPath(
 | 
						|
		const QPainterPath &,
 | 
						|
		Qt::ItemSelectionMode) const {
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
void ItemCanvas::cancelDrawing() {
 | 
						|
	_drawing = false;
 | 
						|
	_contentRect = QRectF();
 | 
						|
	clearPixmap();
 | 
						|
	update();
 | 
						|
}
 | 
						|
 | 
						|
ItemCanvas::~ItemCanvas() {
 | 
						|
	_hq = nullptr;
 | 
						|
	_p = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Editor
 |