mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Added initial implementation of mouse drawing in photo editor.
This commit is contained in:
parent
e1ea833ad6
commit
5b6bddd7fc
10 changed files with 254 additions and 13 deletions
|
@ -510,6 +510,8 @@ PRIVATE
|
||||||
dialogs/dialogs_widget.h
|
dialogs/dialogs_widget.h
|
||||||
editor/editor_crop.cpp
|
editor/editor_crop.cpp
|
||||||
editor/editor_crop.h
|
editor/editor_crop.h
|
||||||
|
editor/editor_paint.cpp
|
||||||
|
editor/editor_paint.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
|
||||||
|
|
133
Telegram/SourceFiles/editor/editor_paint.cpp
Normal file
133
Telegram/SourceFiles/editor/editor_paint.cpp
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
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_paint.h"
|
||||||
|
|
||||||
|
#include "base/event_filter.h"
|
||||||
|
#include "styles/style_boxes.h"
|
||||||
|
|
||||||
|
#include <QGraphicsItemGroup>
|
||||||
|
#include <QGraphicsSceneMouseEvent>
|
||||||
|
#include <QGraphicsView>
|
||||||
|
|
||||||
|
namespace Editor {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kViewStyle = "QGraphicsView {\
|
||||||
|
background-color: transparent;\
|
||||||
|
border: 0px\
|
||||||
|
}"_cs;
|
||||||
|
|
||||||
|
std::shared_ptr<QGraphicsScene> EnsureScene(PhotoModifications &mods) {
|
||||||
|
if (!mods.paint) {
|
||||||
|
mods.paint = std::make_shared<QGraphicsScene>(nullptr);
|
||||||
|
}
|
||||||
|
return mods.paint;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Paint::Paint(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
PhotoModifications &modifications,
|
||||||
|
const QSize &imageSize)
|
||||||
|
: RpWidget(parent)
|
||||||
|
, _scene(EnsureScene(modifications))
|
||||||
|
, _view(base::make_unique_q<QGraphicsView>(_scene.get(), this))
|
||||||
|
, _imageSize(imageSize) {
|
||||||
|
Expects(modifications.paint != nullptr);
|
||||||
|
|
||||||
|
_view->show();
|
||||||
|
_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
_view->setStyleSheet(kViewStyle.utf8());
|
||||||
|
|
||||||
|
_scene->setSceneRect(0, 0, imageSize.width(), imageSize.height());
|
||||||
|
|
||||||
|
initDrawing();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Paint::applyTransform(QRect geometry, int angle, bool flipped) {
|
||||||
|
if (geometry.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setGeometry(geometry);
|
||||||
|
const auto size = geometry.size();
|
||||||
|
|
||||||
|
const auto rotatedImageSize = QMatrix()
|
||||||
|
.rotate(angle)
|
||||||
|
.mapRect(QRect(QPoint(), _imageSize));
|
||||||
|
|
||||||
|
const auto ratioW = size.width() / float64(rotatedImageSize.width())
|
||||||
|
* (flipped ? -1 : 1);
|
||||||
|
const auto ratioH = size.height() / float64(rotatedImageSize.height());
|
||||||
|
|
||||||
|
_view->setTransform(QTransform().scale(ratioW, ratioH).rotate(angle));
|
||||||
|
_view->setGeometry(QRect(QPoint(), size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Paint::initDrawing() {
|
||||||
|
using Result = base::EventFilterResult;
|
||||||
|
|
||||||
|
_brushData.size = 10;
|
||||||
|
_brushData.color = Qt::red;
|
||||||
|
|
||||||
|
auto callback = [=](not_null<QEvent*> event) {
|
||||||
|
const auto type = event->type();
|
||||||
|
const auto isPress = (type == QEvent::GraphicsSceneMousePress);
|
||||||
|
const auto isMove = (type == QEvent::GraphicsSceneMouseMove);
|
||||||
|
const auto isRelease = (type == QEvent::GraphicsSceneMouseRelease);
|
||||||
|
if (!isPress && !isMove && !isRelease) {
|
||||||
|
return Result::Continue;
|
||||||
|
}
|
||||||
|
const auto e = static_cast<QGraphicsSceneMouseEvent*>(event.get());
|
||||||
|
|
||||||
|
const auto &size = _brushData.size;
|
||||||
|
const auto &color = _brushData.color;
|
||||||
|
const auto mousePoint = e->scenePos();
|
||||||
|
if (isPress) {
|
||||||
|
auto dot = _scene->addEllipse(
|
||||||
|
mousePoint.x() - size / 2,
|
||||||
|
mousePoint.y() - size / 2,
|
||||||
|
size,
|
||||||
|
size,
|
||||||
|
QPen(Qt::NoPen),
|
||||||
|
QBrush(color));
|
||||||
|
_brushData.group = _scene->createItemGroup(
|
||||||
|
QList<QGraphicsItem*>{ std::move(dot) });
|
||||||
|
}
|
||||||
|
if (isMove && _brushData.group) {
|
||||||
|
_brushData.group->addToGroup(_scene->addLine(
|
||||||
|
_brushData.lastPoint.x(),
|
||||||
|
_brushData.lastPoint.y(),
|
||||||
|
mousePoint.x(),
|
||||||
|
mousePoint.y(),
|
||||||
|
QPen(color, size, Qt::SolidLine, Qt::RoundCap)));
|
||||||
|
}
|
||||||
|
if (isRelease) {
|
||||||
|
_brushData.group = nullptr;
|
||||||
|
}
|
||||||
|
_brushData.lastPoint = mousePoint;
|
||||||
|
|
||||||
|
return Result::Cancel;
|
||||||
|
};
|
||||||
|
|
||||||
|
base::install_event_filter(this, _scene.get(), std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Paint::applyMode(PhotoEditorMode mode) {
|
||||||
|
setAttribute(
|
||||||
|
Qt::WA_TransparentForMouseEvents,
|
||||||
|
mode == PhotoEditorMode::Transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<QGraphicsScene> Paint::saveScene() const {
|
||||||
|
return _scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Editor
|
49
Telegram/SourceFiles/editor/editor_paint.h
Normal file
49
Telegram/SourceFiles/editor/editor_paint.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
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 "editor/photo_editor_common.h"
|
||||||
|
|
||||||
|
class QGraphicsItemGroup;
|
||||||
|
class QGraphicsView;
|
||||||
|
|
||||||
|
namespace Editor {
|
||||||
|
|
||||||
|
// Paint control.
|
||||||
|
class Paint final : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
Paint(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
PhotoModifications &modifications,
|
||||||
|
const QSize &imageSize);
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<QGraphicsScene> saveScene() const;
|
||||||
|
|
||||||
|
void applyTransform(QRect geometry, int angle, bool flipped);
|
||||||
|
void applyMode(PhotoEditorMode mode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initDrawing();
|
||||||
|
|
||||||
|
const std::shared_ptr<QGraphicsScene> _scene;
|
||||||
|
const base::unique_qptr<QGraphicsView> _view;
|
||||||
|
const QSize _imageSize;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
QPointF lastPoint;
|
||||||
|
|
||||||
|
float size = 1.;
|
||||||
|
QColor color;
|
||||||
|
QGraphicsItemGroup *group;
|
||||||
|
} _brushData;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Editor
|
|
@ -18,11 +18,11 @@ PhotoEditor::PhotoEditor(
|
||||||
std::shared_ptr<QPixmap> photo,
|
std::shared_ptr<QPixmap> photo,
|
||||||
PhotoModifications modifications)
|
PhotoModifications modifications)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _modifications(modifications)
|
, _modifications(std::move(modifications))
|
||||||
, _content(base::make_unique_q<PhotoEditorContent>(
|
, _content(base::make_unique_q<PhotoEditorContent>(
|
||||||
this,
|
this,
|
||||||
photo,
|
photo,
|
||||||
modifications))
|
_modifications))
|
||||||
, _controls(base::make_unique_q<PhotoEditorControls>(this)) {
|
, _controls(base::make_unique_q<PhotoEditorControls>(this)) {
|
||||||
sizeValue(
|
sizeValue(
|
||||||
) | rpl::start_with_next([=](const QSize &size) {
|
) | rpl::start_with_next([=](const QSize &size) {
|
||||||
|
@ -49,10 +49,16 @@ PhotoEditor::PhotoEditor(
|
||||||
_modifications.flipped = !_modifications.flipped;
|
_modifications.flipped = !_modifications.flipped;
|
||||||
_content->applyModifications(_modifications);
|
_content->applyModifications(_modifications);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
_controls->paintModeRequests(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_content->applyMode(PhotoEditorMode::Paint);
|
||||||
|
}, lifetime());
|
||||||
|
_content->applyMode(PhotoEditorMode::Transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhotoEditor::save() {
|
void PhotoEditor::save() {
|
||||||
_modifications.crop = _content->cropRect();
|
_content->save(_modifications);
|
||||||
_done.fire_copy(_modifications);
|
_done.fire_copy(_modifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,12 @@ QImage ImageModified(QImage image, const PhotoModifications &mods) {
|
||||||
if (!mods) {
|
if (!mods) {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
if (mods.paint) {
|
||||||
|
Painter p(&image);
|
||||||
|
PainterHighQualityEnabler hq(p);
|
||||||
|
|
||||||
|
mods.paint->render(&p, image.rect());
|
||||||
|
}
|
||||||
QTransform transform;
|
QTransform transform;
|
||||||
if (mods.flipped) {
|
if (mods.flipped) {
|
||||||
transform.scale(-1, 1);
|
transform.scale(-1, 1);
|
||||||
|
@ -27,4 +33,18 @@ QImage ImageModified(QImage image, const PhotoModifications &mods) {
|
||||||
return newImage;
|
return newImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PhotoModifications::empty() const {
|
||||||
|
return !angle && !flipped && !crop.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
PhotoModifications::operator bool() const {
|
||||||
|
return !empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
PhotoModifications::~PhotoModifications() {
|
||||||
|
if (paint && (paint.use_count() == 1)) {
|
||||||
|
paint->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Editor
|
} // namespace Editor
|
||||||
|
|
|
@ -7,19 +7,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QGraphicsScene>
|
||||||
|
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
|
||||||
|
enum class PhotoEditorMode {
|
||||||
|
Transform,
|
||||||
|
Paint,
|
||||||
|
};
|
||||||
|
|
||||||
struct PhotoModifications {
|
struct PhotoModifications {
|
||||||
int angle = 0;
|
int angle = 0;
|
||||||
bool flipped = false;
|
bool flipped = false;
|
||||||
QRect crop;
|
QRect crop;
|
||||||
|
std::shared_ptr<QGraphicsScene> paint = nullptr;
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const {
|
[[nodiscard]] bool empty() const;
|
||||||
return !angle && !flipped && !crop.isValid();
|
[[nodiscard]] explicit operator bool() const;
|
||||||
}
|
~PhotoModifications();
|
||||||
[[nodiscard]] explicit operator bool() const {
|
|
||||||
return !empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ 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 "editor/editor_crop.h"
|
||||||
|
#include "editor/editor_paint.h"
|
||||||
#include "media/view/media_view_pip.h"
|
#include "media/view/media_view_pip.h"
|
||||||
|
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
@ -20,6 +21,7 @@ PhotoEditorContent::PhotoEditorContent(
|
||||||
std::shared_ptr<QPixmap> photo,
|
std::shared_ptr<QPixmap> photo,
|
||||||
PhotoModifications modifications)
|
PhotoModifications modifications)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
|
, _paint(base::make_unique_q<Paint>(this, modifications, photo->size()))
|
||||||
, _crop(base::make_unique_q<Crop>(this, modifications, photo->size()))
|
, _crop(base::make_unique_q<Crop>(this, modifications, photo->size()))
|
||||||
, _photo(photo)
|
, _photo(photo)
|
||||||
, _modifications(modifications) {
|
, _modifications(modifications) {
|
||||||
|
@ -58,10 +60,12 @@ PhotoEditorContent::PhotoEditorContent(
|
||||||
}
|
}
|
||||||
_imageMatrix.rotate(mods.angle);
|
_imageMatrix.rotate(mods.angle);
|
||||||
|
|
||||||
|
const auto geometry = _imageMatrix.mapRect(_imageRect);
|
||||||
_crop->applyTransform(
|
_crop->applyTransform(
|
||||||
_imageMatrix.mapRect(_imageRect) + _crop->cropMargins(),
|
geometry + _crop->cropMargins(),
|
||||||
mods.angle,
|
mods.angle,
|
||||||
mods.flipped);
|
mods.flipped);
|
||||||
|
_paint->applyTransform(geometry, mods.angle, mods.flipped);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
paintRequest(
|
paintRequest(
|
||||||
|
@ -82,8 +86,17 @@ void PhotoEditorContent::applyModifications(
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect PhotoEditorContent::cropRect() const {
|
void PhotoEditorContent::save(PhotoModifications &modifications) {
|
||||||
return _crop->saveCropRect(_imageRect, _photo->rect());
|
modifications.crop = _crop->saveCropRect(_imageRect, _photo->rect());
|
||||||
|
if (!modifications.paint) {
|
||||||
|
modifications.paint = _paint->saveScene();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhotoEditorContent::applyMode(PhotoEditorMode mode) {
|
||||||
|
_crop->setVisible(mode == PhotoEditorMode::Transform);
|
||||||
|
_paint->applyMode(mode);
|
||||||
|
_mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Editor
|
} // namespace Editor
|
||||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
|
||||||
class Crop;
|
class Crop;
|
||||||
|
class Paint;
|
||||||
|
|
||||||
class PhotoEditorContent final : public Ui::RpWidget {
|
class PhotoEditorContent final : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
|
@ -23,10 +24,12 @@ public:
|
||||||
PhotoModifications modifications);
|
PhotoModifications modifications);
|
||||||
|
|
||||||
void applyModifications(PhotoModifications modifications);
|
void applyModifications(PhotoModifications modifications);
|
||||||
[[nodiscard]] QRect cropRect() const;
|
void applyMode(PhotoEditorMode mode);
|
||||||
|
void save(PhotoModifications &modifications);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
const base::unique_qptr<Paint> _paint;
|
||||||
const base::unique_qptr<Crop> _crop;
|
const base::unique_qptr<Crop> _crop;
|
||||||
const std::shared_ptr<QPixmap> _photo;
|
const std::shared_ptr<QPixmap> _photo;
|
||||||
|
|
||||||
|
@ -34,6 +37,7 @@ private:
|
||||||
|
|
||||||
QRect _imageRect;
|
QRect _imageRect;
|
||||||
QMatrix _imageMatrix;
|
QMatrix _imageMatrix;
|
||||||
|
PhotoEditorMode _mode;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,9 @@ PhotoEditorControls::PhotoEditorControls(
|
||||||
_buttonsContainer,
|
_buttonsContainer,
|
||||||
st::photoEditorRotateButton))
|
st::photoEditorRotateButton))
|
||||||
, _flipButton(base::make_unique_q<Ui::IconButton>(
|
, _flipButton(base::make_unique_q<Ui::IconButton>(
|
||||||
|
_buttonsContainer,
|
||||||
|
st::photoEditorFlipButton))
|
||||||
|
, _paintModeButton(base::make_unique_q<Ui::IconButton>(
|
||||||
_buttonsContainer,
|
_buttonsContainer,
|
||||||
st::photoEditorFlipButton)) {
|
st::photoEditorFlipButton)) {
|
||||||
|
|
||||||
|
@ -84,4 +87,8 @@ rpl::producer<> PhotoEditorControls::flipRequests() const {
|
||||||
return _flipButton->clicks() | rpl::to_empty;
|
return _flipButton->clicks() | rpl::to_empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> PhotoEditorControls::paintModeRequests() const {
|
||||||
|
return _paintModeButton->clicks() | rpl::to_empty;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Editor
|
} // namespace Editor
|
||||||
|
|
|
@ -25,12 +25,14 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<int> rotateRequests() const;
|
[[nodiscard]] rpl::producer<int> rotateRequests() const;
|
||||||
[[nodiscard]] rpl::producer<> flipRequests() const;
|
[[nodiscard]] rpl::producer<> flipRequests() const;
|
||||||
|
[[nodiscard]] rpl::producer<> paintModeRequests() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
const base::unique_qptr<HorizontalContainer> _buttonsContainer;
|
const base::unique_qptr<HorizontalContainer> _buttonsContainer;
|
||||||
const base::unique_qptr<Ui::IconButton> _rotateButton;
|
const base::unique_qptr<Ui::IconButton> _rotateButton;
|
||||||
const base::unique_qptr<Ui::IconButton> _flipButton;
|
const base::unique_qptr<Ui::IconButton> _flipButton;
|
||||||
|
const base::unique_qptr<Ui::IconButton> _paintModeButton;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue