mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Added ability to crop images in photo editor.
This commit is contained in:
parent
85c21ba0e4
commit
e1ea833ad6
8 changed files with 438 additions and 8 deletions
|
@ -508,6 +508,8 @@ PRIVATE
|
||||||
dialogs/dialogs_search_from_controllers.h
|
dialogs/dialogs_search_from_controllers.h
|
||||||
dialogs/dialogs_widget.cpp
|
dialogs/dialogs_widget.cpp
|
||||||
dialogs/dialogs_widget.h
|
dialogs/dialogs_widget.h
|
||||||
|
editor/editor_crop.cpp
|
||||||
|
editor/editor_crop.h
|
||||||
editor/photo_editor.cpp
|
editor/photo_editor.cpp
|
||||||
editor/photo_editor.h
|
editor/photo_editor.h
|
||||||
editor/photo_editor_common.cpp
|
editor/photo_editor_common.cpp
|
||||||
|
|
300
Telegram/SourceFiles/editor/editor_crop.cpp
Normal file
300
Telegram/SourceFiles/editor/editor_crop.cpp
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
/*
|
||||||
|
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/editor_crop.h"
|
||||||
|
|
||||||
|
#include "styles/style_boxes.h"
|
||||||
|
|
||||||
|
namespace Editor {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
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 kEAll = Qt::TopEdge
|
||||||
|
| Qt::LeftEdge
|
||||||
|
| Qt::BottomEdge
|
||||||
|
| Qt::RightEdge;
|
||||||
|
|
||||||
|
std::tuple<int, int, int, int> RectEdges(const QRect &r) {
|
||||||
|
return { r.left(), r.top(), r.left() + r.width(), r.top() + r.height() };
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint PointOfEdge(Qt::Edges e, const QRect &r) {
|
||||||
|
switch(e) {
|
||||||
|
case kETL: return QPoint(r.x(), r.y());
|
||||||
|
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());
|
||||||
|
default: return QPoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Crop::Crop(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
const PhotoModifications &modifications,
|
||||||
|
const QSize &imageSize)
|
||||||
|
: RpWidget(parent)
|
||||||
|
, _pointSize(st::cropPointSize)
|
||||||
|
, _pointSizeH(_pointSize / 2.)
|
||||||
|
, _innerMargins(QMarginsF(_pointSizeH, _pointSizeH, _pointSizeH, _pointSizeH)
|
||||||
|
.toMargins())
|
||||||
|
, _offset(_innerMargins.left(), _innerMargins.top())
|
||||||
|
, _edgePointMargins(_pointSizeH, _pointSizeH, -_pointSizeH, -_pointSizeH) {
|
||||||
|
|
||||||
|
_angle = modifications.angle;
|
||||||
|
_flipped = modifications.flipped;
|
||||||
|
_cropRect = modifications.crop;
|
||||||
|
if (_cropRect.isValid()) {
|
||||||
|
const auto inner = QRect(QPoint(), imageSize);
|
||||||
|
_innerRect = QRect(
|
||||||
|
QPoint(),
|
||||||
|
QMatrix().rotate(-_angle).mapRect(inner).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
setMouseTracking(true);
|
||||||
|
|
||||||
|
paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
p.fillPath(_painterPath, st::photoCropFadeBg);
|
||||||
|
|
||||||
|
paintPoints(p);
|
||||||
|
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::applyTransform(QRect geometry, int angle, bool flipped) {
|
||||||
|
if (geometry.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setGeometry(geometry);
|
||||||
|
|
||||||
|
const auto nowInner = QRect(QPoint(), geometry.size()) - _innerMargins;
|
||||||
|
const auto wasInner = _innerRect.isEmpty() ? nowInner : _innerRect;
|
||||||
|
const auto nowInnerF = QRectF(QPointF(), QSizeF(nowInner.size()));
|
||||||
|
const auto wasInnerF = QRectF(QPointF(), QSizeF(wasInner.size()));
|
||||||
|
|
||||||
|
_innerRect = nowInner;
|
||||||
|
|
||||||
|
if (_cropRect.isEmpty()) {
|
||||||
|
setCropRect(_innerRect.translated(-_offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto angleTo = (angle - _angle) * (flipped ? -1 : 1);
|
||||||
|
const auto flippedChanges = (_flipped != flipped);
|
||||||
|
|
||||||
|
const auto nowInnerCenter = nowInnerF.center();
|
||||||
|
const auto nowInnerRotated = QMatrix()
|
||||||
|
.translate(nowInnerCenter.x(), nowInnerCenter.y())
|
||||||
|
.rotate(-angleTo)
|
||||||
|
.translate(-nowInnerCenter.x(), -nowInnerCenter.y())
|
||||||
|
.mapRect(nowInnerF);
|
||||||
|
|
||||||
|
const auto nowCropRect = resizedCropRect(wasInnerF, nowInnerRotated)
|
||||||
|
.translated(nowInnerRotated.topLeft());
|
||||||
|
|
||||||
|
const auto nowInnerRotatedCenter = nowInnerRotated.center();
|
||||||
|
|
||||||
|
setCropRect(QMatrix()
|
||||||
|
.translate(nowInnerRotatedCenter.x(), nowInnerRotatedCenter.y())
|
||||||
|
.rotate(angleTo)
|
||||||
|
.scale(flippedChanges ? -1 : 1, 1)
|
||||||
|
.translate(-nowInnerRotatedCenter.x(), -nowInnerRotatedCenter.y())
|
||||||
|
.mapRect(nowCropRect)
|
||||||
|
.toRect());
|
||||||
|
|
||||||
|
{
|
||||||
|
// Check boundaries.
|
||||||
|
const auto p = _cropRectPaint.center();
|
||||||
|
computeDownState(p);
|
||||||
|
performMove(p);
|
||||||
|
clearDownState();
|
||||||
|
}
|
||||||
|
|
||||||
|
_flipped = flipped;
|
||||||
|
_angle = angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::paintPoints(Painter &p) {
|
||||||
|
p.save();
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(st::photoCropPointFg);
|
||||||
|
for (const auto &r : ranges::views::values(_edges)) {
|
||||||
|
p.drawRect(r);
|
||||||
|
}
|
||||||
|
p.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::setCropRect(QRect &&rect) {
|
||||||
|
_cropRect = std::move(rect);
|
||||||
|
_cropRectPaint = _cropRect.translated(_offset);
|
||||||
|
updateEdges();
|
||||||
|
|
||||||
|
_painterPath.clear();
|
||||||
|
_painterPath.addRect(_innerRect);
|
||||||
|
_painterPath.addRect(_cropRectPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::setCropRectPaint(QRect &&rect) {
|
||||||
|
rect.translate(-_offset);
|
||||||
|
setCropRect(std::move(rect));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::updateEdges() {
|
||||||
|
const auto &s = _pointSize;
|
||||||
|
const auto &m = _edgePointMargins;
|
||||||
|
const auto &r = _cropRectPaint;
|
||||||
|
for (const auto &e : { kETL, kETR, kEBL, kEBR }) {
|
||||||
|
_edges[e] = QRectF(PointOfEdge(e, r), QSize(s, s)) + m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::Edges Crop::mouseState(const QPoint &p) {
|
||||||
|
for (const auto &[e, r] : _edges) {
|
||||||
|
if (r.contains(p)) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_cropRectPaint.contains(p)) {
|
||||||
|
return kEAll;
|
||||||
|
}
|
||||||
|
return Qt::Edges();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::mousePressEvent(QMouseEvent *e) {
|
||||||
|
computeDownState(e->pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
|
clearDownState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::computeDownState(const QPoint &p) {
|
||||||
|
const auto edge = mouseState(p);
|
||||||
|
const auto &inner = _innerRect;
|
||||||
|
const auto &crop = _cropRectPaint;
|
||||||
|
const auto [iLeft, iTop, iRight, iBottom] = RectEdges(inner);
|
||||||
|
const auto [cLeft, cTop, cRight, cBottom] = RectEdges(crop);
|
||||||
|
_down = InfoAtDown{
|
||||||
|
.rect = crop,
|
||||||
|
.edge = edge,
|
||||||
|
.point = (p - PointOfEdge(edge, crop)),
|
||||||
|
.borders = InfoAtDown::Borders{
|
||||||
|
.left = iLeft - cLeft,
|
||||||
|
.right = iRight - cRight,
|
||||||
|
.top = iTop - cTop,
|
||||||
|
.bottom = iBottom - cBottom,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::clearDownState() {
|
||||||
|
_down = InfoAtDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::performCrop(const QPoint &pos) {
|
||||||
|
const auto &crop = _down.rect;
|
||||||
|
const auto &pressedEdge = _down.edge;
|
||||||
|
const auto hasLeft = (pressedEdge & Qt::LeftEdge);
|
||||||
|
const auto hasTop = (pressedEdge & Qt::TopEdge);
|
||||||
|
const auto hasRight = (pressedEdge & Qt::RightEdge);
|
||||||
|
const auto hasBottom = (pressedEdge & Qt::BottomEdge);
|
||||||
|
const auto diff = [&] {
|
||||||
|
const auto diff = pos - PointOfEdge(pressedEdge, crop) - _down.point;
|
||||||
|
const auto hFactor = hasLeft ? 1 : -1;
|
||||||
|
const auto vFactor = hasTop ? 1 : -1;
|
||||||
|
const auto &borders = _down.borders;
|
||||||
|
|
||||||
|
const auto hMin = hFactor * crop.width() - hFactor * st::cropMinSize;
|
||||||
|
const auto vMin = vFactor * crop.height() - vFactor * st::cropMinSize;
|
||||||
|
|
||||||
|
const auto x = std::clamp(
|
||||||
|
diff.x(),
|
||||||
|
hasLeft ? borders.left : hMin,
|
||||||
|
hasLeft ? hMin : borders.right);
|
||||||
|
const auto y = std::clamp(
|
||||||
|
diff.y(),
|
||||||
|
hasTop ? borders.top : vMin,
|
||||||
|
hasTop ? vMin : borders.bottom);
|
||||||
|
if (_keepAspectRatio) {
|
||||||
|
const auto minDiff = std::min(std::abs(x), std::abs(y));
|
||||||
|
return QPoint(minDiff * hFactor, minDiff * vFactor);
|
||||||
|
}
|
||||||
|
return QPoint(x, y);
|
||||||
|
}();
|
||||||
|
setCropRectPaint(crop - QMargins(
|
||||||
|
hasLeft ? diff.x() : 0,
|
||||||
|
hasTop ? diff.y() : 0,
|
||||||
|
hasRight ? -diff.x() : 0,
|
||||||
|
hasBottom ? -diff.y() : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::performMove(const QPoint &pos) {
|
||||||
|
const auto &inner = _down.rect;
|
||||||
|
const auto &b = _down.borders;
|
||||||
|
const auto diffX = std::clamp(pos.x() - _down.point.x(), b.left, b.right);
|
||||||
|
const auto diffY = std::clamp(pos.y() - _down.point.y(), b.top, b.bottom);
|
||||||
|
setCropRectPaint(inner.translated(diffX, diffY));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crop::mouseMoveEvent(QMouseEvent *e) {
|
||||||
|
const auto pos = e->pos();
|
||||||
|
const auto pressedEdge = _down.edge;
|
||||||
|
|
||||||
|
if (pressedEdge) {
|
||||||
|
if (pressedEdge == kEAll) {
|
||||||
|
performMove(pos);
|
||||||
|
} else if (pressedEdge) {
|
||||||
|
performCrop(pos);
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto edge = pressedEdge ? pressedEdge : mouseState(pos);
|
||||||
|
|
||||||
|
const auto cursor = ((edge == kETL) || (edge == kEBR))
|
||||||
|
? style::cur_sizefdiag
|
||||||
|
: ((edge == kETR) || (edge == kEBL))
|
||||||
|
? style::cur_sizebdiag
|
||||||
|
: (edge == kEAll)
|
||||||
|
? style::cur_sizeall
|
||||||
|
: style::cur_default;
|
||||||
|
setCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect Crop::innerRect() const {
|
||||||
|
return _innerRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
style::margins Crop::cropMargins() const {
|
||||||
|
return _innerMargins;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect Crop::saveCropRect(const QRect &from, const QRect &to) {
|
||||||
|
return resizedCropRect(QRectF(from), QRectF(to)).toRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF Crop::resizedCropRect(const QRectF &from, const QRectF &to) {
|
||||||
|
const auto ratioW = to.width() / float64(from.width());
|
||||||
|
const auto ratioH = to.height() / float64(from.height());
|
||||||
|
const auto &min = float64(st::cropMinSize);
|
||||||
|
const auto &r = _cropRect;
|
||||||
|
|
||||||
|
return QRectF(
|
||||||
|
r.x() * ratioW,
|
||||||
|
r.y() * ratioH,
|
||||||
|
std::max(r.width() * ratioW, min),
|
||||||
|
std::max(r.height() * ratioH, min));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Editor
|
95
Telegram/SourceFiles/editor/editor_crop.h
Normal file
95
Telegram/SourceFiles/editor/editor_crop.h
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/rp_widget.h"
|
||||||
|
|
||||||
|
#include "base/flat_map.h"
|
||||||
|
#include "editor/photo_editor_common.h"
|
||||||
|
|
||||||
|
namespace Editor {
|
||||||
|
|
||||||
|
// Crop control.
|
||||||
|
class Crop final : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
Crop(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
const PhotoModifications &modifications,
|
||||||
|
const QSize &imageSize);
|
||||||
|
|
||||||
|
void applyTransform(QRect geometry, int angle, bool flipped);
|
||||||
|
[[nodiscard]] QRect innerRect() const;
|
||||||
|
[[nodiscard]] QRect saveCropRect(
|
||||||
|
const QRect &from,
|
||||||
|
const QRect &to);
|
||||||
|
[[nodiscard]] style::margins cropMargins() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mousePressEvent(QMouseEvent *e) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct InfoAtDown {
|
||||||
|
QRect rect;
|
||||||
|
Qt::Edges edge = 0;
|
||||||
|
QPoint point;
|
||||||
|
|
||||||
|
struct Borders {
|
||||||
|
int left = 0;
|
||||||
|
int right = 0;
|
||||||
|
int top = 0;
|
||||||
|
int bottom = 0;
|
||||||
|
} borders;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] QRectF resizedCropRect(
|
||||||
|
const QRectF &from,
|
||||||
|
const QRectF &to);
|
||||||
|
|
||||||
|
void paintPoints(Painter &p);
|
||||||
|
|
||||||
|
void updateEdges();
|
||||||
|
[[nodiscard]] QPoint pointOfEdge(Qt::Edges e) const;
|
||||||
|
void setCropRect(QRect &&rect);
|
||||||
|
void setCropRectPaint(QRect &&rect);
|
||||||
|
void rotate(bool clockwise = true);
|
||||||
|
|
||||||
|
void computeDownState(const QPoint &p);
|
||||||
|
void clearDownState();
|
||||||
|
[[nodiscard]] Qt::Edges mouseState(const QPoint &p);
|
||||||
|
void performCrop(const QPoint &pos);
|
||||||
|
void performMove(const QPoint &pos);
|
||||||
|
|
||||||
|
const int _pointSize;
|
||||||
|
const float _pointSizeH;
|
||||||
|
const style::margins _innerMargins;
|
||||||
|
const QPoint _offset;
|
||||||
|
const QMarginsF _edgePointMargins;
|
||||||
|
|
||||||
|
base::flat_map<Qt::Edges, QRectF> _edges;
|
||||||
|
|
||||||
|
// Is translated with the inner indentation.
|
||||||
|
QRect _cropRectPaint;
|
||||||
|
// Is not.
|
||||||
|
QRect _cropRect;
|
||||||
|
|
||||||
|
QRect _innerRect;
|
||||||
|
|
||||||
|
QPainterPath _painterPath;
|
||||||
|
|
||||||
|
InfoAtDown _down;
|
||||||
|
|
||||||
|
int _angle = 0;
|
||||||
|
bool _flipped = false;
|
||||||
|
|
||||||
|
bool _keepAspectRatio = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Editor
|
|
@ -52,6 +52,7 @@ PhotoEditor::PhotoEditor(
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhotoEditor::save() {
|
void PhotoEditor::save() {
|
||||||
|
_modifications.crop = _content->cropRect();
|
||||||
_done.fire_copy(_modifications);
|
_done.fire_copy(_modifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,11 @@ QImage ImageModified(QImage image, const PhotoModifications &mods) {
|
||||||
if (mods.angle) {
|
if (mods.angle) {
|
||||||
transform.rotate(mods.angle);
|
transform.rotate(mods.angle);
|
||||||
}
|
}
|
||||||
return image.transformed(transform);
|
auto newImage = image.transformed(transform);
|
||||||
|
if (mods.crop.isValid()) {
|
||||||
|
newImage = newImage.copy(mods.crop);
|
||||||
|
}
|
||||||
|
return newImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Editor
|
} // namespace Editor
|
||||||
|
|
|
@ -12,9 +12,10 @@ namespace Editor {
|
||||||
struct PhotoModifications {
|
struct PhotoModifications {
|
||||||
int angle = 0;
|
int angle = 0;
|
||||||
bool flipped = false;
|
bool flipped = false;
|
||||||
|
QRect crop;
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const {
|
[[nodiscard]] bool empty() const {
|
||||||
return !angle && !flipped;
|
return !angle && !flipped && !crop.isValid();
|
||||||
}
|
}
|
||||||
[[nodiscard]] explicit operator bool() const {
|
[[nodiscard]] explicit operator bool() const {
|
||||||
return !empty();
|
return !empty();
|
||||||
|
|
|
@ -7,15 +7,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "editor/photo_editor_content.h"
|
#include "editor/photo_editor_content.h"
|
||||||
|
|
||||||
|
#include "editor/editor_crop.h"
|
||||||
#include "media/view/media_view_pip.h"
|
#include "media/view/media_view_pip.h"
|
||||||
|
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
|
||||||
|
using Media::View::FlipSizeByRotation;
|
||||||
|
using Media::View::RotatedRect;
|
||||||
|
|
||||||
PhotoEditorContent::PhotoEditorContent(
|
PhotoEditorContent::PhotoEditorContent(
|
||||||
not_null<Ui::RpWidget*> parent,
|
not_null<Ui::RpWidget*> parent,
|
||||||
std::shared_ptr<QPixmap> photo,
|
std::shared_ptr<QPixmap> photo,
|
||||||
PhotoModifications modifications)
|
PhotoModifications modifications)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
|
, _crop(base::make_unique_q<Crop>(this, modifications, photo->size()))
|
||||||
|
, _photo(photo)
|
||||||
, _modifications(modifications) {
|
, _modifications(modifications) {
|
||||||
|
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
|
@ -23,14 +29,20 @@ PhotoEditorContent::PhotoEditorContent(
|
||||||
sizeValue()
|
sizeValue()
|
||||||
) | rpl::start_with_next([=](
|
) | rpl::start_with_next([=](
|
||||||
const PhotoModifications &mods, const QSize &size) {
|
const PhotoModifications &mods, const QSize &size) {
|
||||||
const auto rotatedSize =
|
if (size.isEmpty()) {
|
||||||
Media::View::FlipSizeByRotation(size, mods.angle);
|
return;
|
||||||
|
}
|
||||||
const auto imageSize = [&] {
|
const auto imageSize = [&] {
|
||||||
const auto originalSize = photo->size() / cIntRetinaFactor();
|
const auto rotatedSize =
|
||||||
if ((originalSize.width() > rotatedSize.width())
|
FlipSizeByRotation(size, mods.angle);
|
||||||
|| (originalSize.height() > rotatedSize.height())) {
|
const auto m = _crop->cropMargins();
|
||||||
|
const auto sizeForCrop = rotatedSize
|
||||||
|
- QSize(m.left() + m.right(), m.top() + m.bottom());
|
||||||
|
const auto originalSize = photo->size();
|
||||||
|
if ((originalSize.width() > sizeForCrop.width())
|
||||||
|
|| (originalSize.height() > sizeForCrop.height())) {
|
||||||
return originalSize.scaled(
|
return originalSize.scaled(
|
||||||
rotatedSize,
|
sizeForCrop,
|
||||||
Qt::KeepAspectRatio);
|
Qt::KeepAspectRatio);
|
||||||
}
|
}
|
||||||
return originalSize;
|
return originalSize;
|
||||||
|
@ -45,6 +57,11 @@ PhotoEditorContent::PhotoEditorContent(
|
||||||
_imageMatrix.scale(-1, 1);
|
_imageMatrix.scale(-1, 1);
|
||||||
}
|
}
|
||||||
_imageMatrix.rotate(mods.angle);
|
_imageMatrix.rotate(mods.angle);
|
||||||
|
|
||||||
|
_crop->applyTransform(
|
||||||
|
_imageMatrix.mapRect(_imageRect) + _crop->cropMargins(),
|
||||||
|
mods.angle,
|
||||||
|
mods.flipped);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
paintRequest(
|
paintRequest(
|
||||||
|
@ -65,4 +82,8 @@ void PhotoEditorContent::applyModifications(
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect PhotoEditorContent::cropRect() const {
|
||||||
|
return _crop->saveCropRect(_imageRect, _photo->rect());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Editor
|
} // namespace Editor
|
||||||
|
|
|
@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
|
||||||
|
class Crop;
|
||||||
|
|
||||||
class PhotoEditorContent final : public Ui::RpWidget {
|
class PhotoEditorContent final : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
PhotoEditorContent(
|
PhotoEditorContent(
|
||||||
|
@ -21,9 +23,13 @@ public:
|
||||||
PhotoModifications modifications);
|
PhotoModifications modifications);
|
||||||
|
|
||||||
void applyModifications(PhotoModifications modifications);
|
void applyModifications(PhotoModifications modifications);
|
||||||
|
[[nodiscard]] QRect cropRect() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
const base::unique_qptr<Crop> _crop;
|
||||||
|
const std::shared_ptr<QPixmap> _photo;
|
||||||
|
|
||||||
rpl::variable<PhotoModifications> _modifications;
|
rpl::variable<PhotoModifications> _modifications;
|
||||||
|
|
||||||
QRect _imageRect;
|
QRect _imageRect;
|
||||||
|
|
Loading…
Add table
Reference in a new issue