mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
[img-editor] Improved crop overlay with 3x3 grid.
This commit is contained in:
@@ -19,6 +19,10 @@ constexpr auto kETL = Qt::TopEdge | Qt::LeftEdge;
|
||||
constexpr auto kETR = Qt::TopEdge | Qt::RightEdge;
|
||||
constexpr auto kEBL = Qt::BottomEdge | Qt::LeftEdge;
|
||||
constexpr auto kEBR = Qt::BottomEdge | Qt::RightEdge;
|
||||
constexpr auto kEL = Qt::Edges(Qt::LeftEdge);
|
||||
constexpr auto kER = Qt::Edges(Qt::RightEdge);
|
||||
constexpr auto kET = Qt::Edges(Qt::TopEdge);
|
||||
constexpr auto kEB = Qt::Edges(Qt::BottomEdge);
|
||||
constexpr auto kEAll = Qt::TopEdge
|
||||
| Qt::LeftEdge
|
||||
| Qt::BottomEdge
|
||||
@@ -34,6 +38,10 @@ QPoint PointOfEdge(Qt::Edges e, const QRectF &r) {
|
||||
case kETR: return QPoint(r.x() + r.width(), r.y());
|
||||
case kEBL: return QPoint(r.x(), r.y() + r.height());
|
||||
case kEBR: return QPoint(r.x() + r.width(), r.y() + r.height());
|
||||
case kEL: return QPoint(r.x(), r.y() + (r.height() / 2.));
|
||||
case kER: return QPoint(r.x() + r.width(), r.y() + (r.height() / 2.));
|
||||
case kET: return QPoint(r.x() + (r.width() / 2.), r.y());
|
||||
case kEB: return QPoint(r.x() + (r.width() / 2.), r.y() + r.height());
|
||||
default: return QPoint();
|
||||
}
|
||||
}
|
||||
@@ -83,7 +91,12 @@ Crop::Crop(
|
||||
auto p = QPainter(this);
|
||||
|
||||
p.fillPath(_painterPath, st::photoCropFadeBg);
|
||||
paintPoints(p);
|
||||
paintFrame(p);
|
||||
const auto gridOpacity = _gridOpacityAnimation.value(
|
||||
_gridVisible ? 1. : 0.);
|
||||
if (gridOpacity > 0.) {
|
||||
paintGrid(p, gridOpacity);
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
}
|
||||
@@ -140,12 +153,103 @@ void Crop::applyTransform(
|
||||
}
|
||||
}
|
||||
|
||||
void Crop::paintPoints(QPainter &p) {
|
||||
QPainterPath Crop::cropPath() const {
|
||||
auto result = QPainterPath();
|
||||
if (_data.cropType == EditorData::CropType::Ellipse) {
|
||||
result.addEllipse(_cropPaint);
|
||||
} else if (_data.cropType == EditorData::CropType::RoundedRect) {
|
||||
const auto radius = std::min(_cropPaint.width(), _cropPaint.height())
|
||||
* Ui::ForumUserpicRadiusMultiplier();
|
||||
result.addRoundedRect(_cropPaint, radius, radius);
|
||||
} else {
|
||||
result.addRect(_cropPaint);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Crop::paintFrame(QPainter &p) {
|
||||
const auto framePath = cropPath();
|
||||
auto frameStroker = QPainterPathStroker();
|
||||
frameStroker.setWidth(st::lineWidth * 2);
|
||||
frameStroker.setJoinStyle(Qt::MiterJoin);
|
||||
frameStroker.setCapStyle(Qt::SquareCap);
|
||||
auto frameShape = frameStroker.createStroke(framePath);
|
||||
p.save();
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::photoCropPointFg);
|
||||
for (const auto &r : ranges::views::values(_edges)) {
|
||||
p.drawRect(r);
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
p.fillPath(frameShape, st::photoCropPointFg);
|
||||
if (_data.cropType == EditorData::CropType::Rect) {
|
||||
const auto cornerLength = std::min(
|
||||
float64(st::photoEditorCropPointSize * 2),
|
||||
std::min(_cropPaint.width(), _cropPaint.height()) / 2.);
|
||||
if (cornerLength > 0.) {
|
||||
auto cornerLines = QPainterPath();
|
||||
cornerLines.moveTo(_cropPaint.left(), _cropPaint.top());
|
||||
cornerLines.lineTo(
|
||||
_cropPaint.left() + cornerLength,
|
||||
_cropPaint.top());
|
||||
cornerLines.moveTo(_cropPaint.left(), _cropPaint.top());
|
||||
cornerLines.lineTo(
|
||||
_cropPaint.left(),
|
||||
_cropPaint.top() + cornerLength);
|
||||
cornerLines.moveTo(_cropPaint.right(), _cropPaint.top());
|
||||
cornerLines.lineTo(
|
||||
_cropPaint.right() - cornerLength,
|
||||
_cropPaint.top());
|
||||
cornerLines.moveTo(_cropPaint.right(), _cropPaint.top());
|
||||
cornerLines.lineTo(
|
||||
_cropPaint.right(),
|
||||
_cropPaint.top() + cornerLength);
|
||||
cornerLines.moveTo(_cropPaint.left(), _cropPaint.bottom());
|
||||
cornerLines.lineTo(
|
||||
_cropPaint.left() + cornerLength,
|
||||
_cropPaint.bottom());
|
||||
cornerLines.moveTo(_cropPaint.left(), _cropPaint.bottom());
|
||||
cornerLines.lineTo(
|
||||
_cropPaint.left(),
|
||||
_cropPaint.bottom() - cornerLength);
|
||||
cornerLines.moveTo(_cropPaint.right(), _cropPaint.bottom());
|
||||
cornerLines.lineTo(
|
||||
_cropPaint.right() - cornerLength,
|
||||
_cropPaint.bottom());
|
||||
cornerLines.moveTo(_cropPaint.right(), _cropPaint.bottom());
|
||||
cornerLines.lineTo(
|
||||
_cropPaint.right(),
|
||||
_cropPaint.bottom() - cornerLength);
|
||||
|
||||
auto cornerStroker = QPainterPathStroker();
|
||||
cornerStroker.setWidth(st::lineWidth * 4);
|
||||
cornerStroker.setJoinStyle(Qt::MiterJoin);
|
||||
cornerStroker.setCapStyle(Qt::SquareCap);
|
||||
auto cornerColor = st::photoCropPointFg->c;
|
||||
cornerColor.setAlpha(255);
|
||||
p.fillPath(cornerStroker.createStroke(cornerLines), cornerColor);
|
||||
}
|
||||
}
|
||||
p.restore();
|
||||
}
|
||||
|
||||
void Crop::paintGrid(QPainter &p, float64 opacity) {
|
||||
if ((_cropPaint.width() <= 0.) || (_cropPaint.height() <= 0.)) {
|
||||
return;
|
||||
}
|
||||
p.save();
|
||||
p.setOpacity(opacity);
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
p.setClipPath(cropPath());
|
||||
auto pen = QPen(st::photoCropPointFg, st::lineWidth);
|
||||
pen.setCapStyle(Qt::FlatCap);
|
||||
p.setPen(pen);
|
||||
const auto horizontalStep = _cropPaint.width() / 3.;
|
||||
const auto verticalStep = _cropPaint.height() / 3.;
|
||||
for (auto i = 1; i < 3; ++i) {
|
||||
const auto x = _cropPaint.left() + (horizontalStep * i);
|
||||
const auto y = _cropPaint.top() + (verticalStep * i);
|
||||
p.drawLine(
|
||||
QPointF(x, _cropPaint.top()),
|
||||
QPointF(x, _cropPaint.bottom()));
|
||||
p.drawLine(
|
||||
QPointF(_cropPaint.left(), y),
|
||||
QPointF(_cropPaint.right(), y));
|
||||
}
|
||||
p.restore();
|
||||
}
|
||||
@@ -157,15 +261,7 @@ void Crop::setCropPaint(QRectF &&rect) {
|
||||
|
||||
_painterPath.clear();
|
||||
_painterPath.addRect(_innerRect);
|
||||
if (_data.cropType == EditorData::CropType::Ellipse) {
|
||||
_painterPath.addEllipse(_cropPaint);
|
||||
} else if (_data.cropType == EditorData::CropType::RoundedRect) {
|
||||
const auto radius = std::min(_cropPaint.width(), _cropPaint.height())
|
||||
* Ui::ForumUserpicRadiusMultiplier();
|
||||
_painterPath.addRoundedRect(_cropPaint, radius, radius);
|
||||
} else {
|
||||
_painterPath.addRect(_cropPaint);
|
||||
}
|
||||
_painterPath.addPath(cropPath());
|
||||
}
|
||||
|
||||
void Crop::convertCropPaintToOriginal() {
|
||||
@@ -193,15 +289,46 @@ void Crop::updateEdges() {
|
||||
const auto &s = _pointSize;
|
||||
const auto &m = _edgePointMargins;
|
||||
const auto &r = _cropPaint;
|
||||
const auto sideWidth = std::max(0., r.width() - s);
|
||||
const auto sideHeight = std::max(0., r.height() - s);
|
||||
for (const auto &e : { kETL, kETR, kEBL, kEBR }) {
|
||||
_edges[e] = QRectF(PointOfEdge(e, r), QSize(s, s)) + m;
|
||||
}
|
||||
if (!_keepAspectRatio) {
|
||||
_edges[kEL] = QRectF(
|
||||
r.left() - _pointSizeH,
|
||||
r.top() + _pointSizeH,
|
||||
s,
|
||||
sideHeight);
|
||||
_edges[kER] = QRectF(
|
||||
r.right() - _pointSizeH,
|
||||
r.top() + _pointSizeH,
|
||||
s,
|
||||
sideHeight);
|
||||
_edges[kET] = QRectF(
|
||||
r.left() + _pointSizeH,
|
||||
r.top() - _pointSizeH,
|
||||
sideWidth,
|
||||
s);
|
||||
_edges[kEB] = QRectF(
|
||||
r.left() + _pointSizeH,
|
||||
r.bottom() - _pointSizeH,
|
||||
sideWidth,
|
||||
s);
|
||||
} else {
|
||||
_edges.erase(kEL);
|
||||
_edges.erase(kER);
|
||||
_edges.erase(kET);
|
||||
_edges.erase(kEB);
|
||||
}
|
||||
}
|
||||
|
||||
Qt::Edges Crop::mouseState(const QPoint &p) {
|
||||
for (const auto &[e, r] : _edges) {
|
||||
if (r.contains(p)) {
|
||||
return e;
|
||||
for (const auto &e : { kETL, kETR, kEBL, kEBR, kEL, kER, kET, kEB }) {
|
||||
if (const auto i = _edges.find(e); i != end(_edges)) {
|
||||
if (i->second.contains(p)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_cropPaint.contains(p)) {
|
||||
@@ -212,9 +339,16 @@ Qt::Edges Crop::mouseState(const QPoint &p) {
|
||||
|
||||
void Crop::mousePressEvent(QMouseEvent *e) {
|
||||
computeDownState(e->pos());
|
||||
if (_down.edge) {
|
||||
setGridVisible(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Crop::mouseReleaseEvent(QMouseEvent *e) {
|
||||
const auto hadEdge = bool(_down.edge);
|
||||
if (hadEdge) {
|
||||
setGridVisible(false, true);
|
||||
}
|
||||
clearDownState();
|
||||
convertCropPaintToOriginal();
|
||||
}
|
||||
@@ -263,6 +397,24 @@ void Crop::clearDownState() {
|
||||
_down = InfoAtDown();
|
||||
}
|
||||
|
||||
void Crop::setGridVisible(bool visible, bool animated) {
|
||||
if ((_gridVisible == visible) && !_gridOpacityAnimation.animating()) {
|
||||
return;
|
||||
}
|
||||
const auto from = _gridOpacityAnimation.value(_gridVisible ? 1. : 0.);
|
||||
const auto to = visible ? 1. : 0.;
|
||||
_gridVisible = visible;
|
||||
_gridOpacityAnimation.stop();
|
||||
if (animated) {
|
||||
_gridOpacityAnimation.start(
|
||||
[=] { update(); },
|
||||
from,
|
||||
to,
|
||||
st::photoEditorBarAnimationDuration * std::abs(to - from));
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void Crop::performCrop(const QPoint &pos) {
|
||||
const auto &crop = _down.rect;
|
||||
const auto &pressedEdge = _down.edge;
|
||||
@@ -338,6 +490,10 @@ void Crop::mouseMoveEvent(QMouseEvent *e) {
|
||||
? style::cur_sizefdiag
|
||||
: ((edge == kETR) || (edge == kEBL))
|
||||
? style::cur_sizebdiag
|
||||
: ((edge == kEL) || (edge == kER))
|
||||
? style::cur_sizehor
|
||||
: ((edge == kET) || (edge == kEB))
|
||||
? style::cur_sizever
|
||||
: (edge == kEAll)
|
||||
? style::cur_sizeall
|
||||
: style::cur_default;
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/effects/animations.h"
|
||||
|
||||
#include "base/flat_map.h"
|
||||
#include "editor/photo_editor_common.h"
|
||||
@@ -51,7 +52,10 @@ private:
|
||||
} borders;
|
||||
};
|
||||
|
||||
void paintPoints(QPainter &p);
|
||||
void paintFrame(QPainter &p);
|
||||
void paintGrid(QPainter &p, float64 opacity);
|
||||
void setGridVisible(bool visible, bool animated);
|
||||
[[nodiscard]] QPainterPath cropPath() const;
|
||||
|
||||
void updateEdges();
|
||||
[[nodiscard]] QPoint pointOfEdge(Qt::Edges e) const;
|
||||
@@ -86,9 +90,11 @@ private:
|
||||
QPainterPath _painterPath;
|
||||
|
||||
InfoAtDown _down;
|
||||
Ui::Animations::Simple _gridOpacityAnimation;
|
||||
|
||||
int _angle = 0;
|
||||
bool _flipped = false;
|
||||
bool _gridVisible = false;
|
||||
|
||||
bool _keepAspectRatio = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user