File indexing completed on 2025-02-02 04:26:09
0001 /* 0002 * SPDX-FileCopyrightText: 2022 Marco Martin <mart@kde.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include "AnnotationTool.h" 0010 #include "History.h" 0011 0012 #include <QColor> 0013 #include <QFont> 0014 #include <QImage> 0015 #include <QMatrix4x4> 0016 #include <QObject> 0017 #include <QVariant> 0018 0019 class AnnotationTool; 0020 class SelectedItemWrapper; 0021 class QPainter; 0022 0023 /** 0024 * This class is used to render an image with annotations. The annotations are vector graphics 0025 * and image effects created from a stack of history items that can be undone or redone. 0026 * `paint()` and `renderToImage()` will be used by clients (e.g., AnnotationViewport) to render 0027 * their own content. There can be any amount of clients sharing the same AnnotationDocument. 0028 */ 0029 class AnnotationDocument : public QObject 0030 { 0031 Q_OBJECT 0032 Q_PROPERTY(AnnotationTool *tool READ tool CONSTANT) 0033 Q_PROPERTY(SelectedItemWrapper *selectedItem READ selectedItemWrapper NOTIFY selectedItemWrapperChanged) 0034 0035 Q_PROPERTY(int redoStackDepth READ redoStackDepth NOTIFY redoStackDepthChanged) 0036 Q_PROPERTY(int undoStackDepth READ undoStackDepth NOTIFY undoStackDepthChanged) 0037 Q_PROPERTY(QSizeF canvasSize READ canvasSize NOTIFY canvasSizeChanged) 0038 Q_PROPERTY(QSizeF imageSize READ imageSize NOTIFY imageSizeChanged) 0039 Q_PROPERTY(qreal imageDpr READ imageDpr NOTIFY imageDprChanged) 0040 0041 public: 0042 enum class ContinueOption { 0043 NoOptions = 0b00, 0044 SnapAngle = 0b01, 0045 CenterResize = 0b10 0046 }; 0047 Q_DECLARE_FLAGS(ContinueOptions, ContinueOption) 0048 Q_FLAG(ContinueOption) 0049 0050 explicit AnnotationDocument(QObject *parent = nullptr); 0051 ~AnnotationDocument(); 0052 0053 AnnotationTool *tool() const; 0054 SelectedItemWrapper *selectedItemWrapper() const; 0055 0056 int undoStackDepth() const; 0057 int redoStackDepth() const; 0058 0059 QSizeF canvasSize() const; 0060 0061 /// Image size in raw pixels 0062 QSizeF imageSize() const; 0063 0064 /// Image device pixel ratio 0065 qreal imageDpr() const; 0066 0067 /// Set the base image. Cannot be undone. 0068 void setImage(const QImage &image); 0069 0070 /// Remove annotations that do not intersect with the rectangle and crop the image. 0071 /// Cannot be undone. 0072 void cropCanvas(const QRectF &cropRect); 0073 0074 /// Clear all annotations. Cannot be undone. 0075 void clearAnnotations(); 0076 0077 /// Clear all annotations and the image. Cannot be undone. 0078 void clear(); 0079 0080 struct Viewport { 0081 QRectF rect; 0082 qreal scale = 1; 0083 }; 0084 0085 // Paint the base image. 0086 void paintBaseImage(QPainter *painter, const Viewport &viewport) const; 0087 // Paint the annotations. If the span is not set, all annotations will be painted. 0088 void paintAnnotations(QPainter *painter, const Viewport &viewport, std::optional<History::ConstSpan> span = {}) const; 0089 // Paint the base image and annotations. 0090 void paint(QPainter *painter, const Viewport &viewport, std::optional<History::ConstSpan> span = {}) const; 0091 QImage renderToImage(const Viewport &viewport, std::optional<History::ConstSpan> span = {}) const; 0092 QImage renderToImage(std::optional<History::ConstSpan> span = {}) const; 0093 0094 // True when there is an item at the end of the undo stack and it is invalid. 0095 bool isCurrentItemValid() const; 0096 0097 HistoryItem::shared_ptr popCurrentItem(); 0098 0099 // The first item with a mouse path intersecting the specified rectangle. 0100 // The rectangle is meant to be used as a way to make selecting an item more forgiving 0101 // by adding margins around the center of where the actual target point is. 0102 HistoryItem::const_shared_ptr itemAt(const QRectF &rect) const; 0103 0104 Q_INVOKABLE void undo(); 0105 Q_INVOKABLE void redo(); 0106 0107 // For starting a new item 0108 void beginItem(const QPointF &point); 0109 void continueItem(const QPointF &point, AnnotationDocument::ContinueOptions options = ContinueOption::NoOptions); 0110 void finishItem(); 0111 0112 // For managing an existing item 0113 Q_INVOKABLE void selectItem(const QRectF &rect); 0114 Q_INVOKABLE void deselectItem(); 0115 Q_INVOKABLE void deleteSelectedItem(); 0116 0117 Q_SIGNALS: 0118 void selectedItemWrapperChanged(); 0119 void undoStackDepthChanged(); 0120 void redoStackDepthChanged(); 0121 void canvasSizeChanged(); 0122 void imageSizeChanged(); 0123 void imageDprChanged(); 0124 0125 void repaintNeeded(const QRectF &area = {}); 0126 0127 private: 0128 friend class SelectedItemWrapper; 0129 0130 void addItem(const HistoryItem::shared_ptr &item); 0131 void emitRepaintNeededUnlessEmpty(const QRectF &area); 0132 0133 AnnotationTool *m_tool; 0134 SelectedItemWrapper *m_selectedItemWrapper; 0135 0136 QRectF m_canvasRect; 0137 QImage m_image; 0138 // A temporary version of the item we want to edit so we can modify at will. This will be used 0139 // instead of the original item when rendering, but the original item will remain in history 0140 // until the changes are committed. 0141 HistoryItem::shared_ptr m_tempItem; 0142 History m_history; 0143 }; 0144 0145 /** 0146 * When the user selects an existing shape with the mouse, this wraps all the parameters of the associated item, so that they can be modified from QML 0147 */ 0148 class SelectedItemWrapper : public QObject 0149 { 0150 Q_OBJECT 0151 Q_PROPERTY(bool hasSelection READ hasSelection CONSTANT) 0152 Q_PROPERTY(AnnotationTool::Options options READ options CONSTANT) 0153 Q_PROPERTY(int strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged) 0154 Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged) 0155 Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) 0156 Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) 0157 Q_PROPERTY(QColor fontColor READ fontColor WRITE setFontColor NOTIFY fontColorChanged) 0158 Q_PROPERTY(int number READ number WRITE setNumber NOTIFY numberChanged) 0159 Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) 0160 Q_PROPERTY(bool shadow READ hasShadow WRITE setShadow NOTIFY shadowChanged) 0161 Q_PROPERTY(QPainterPath mousePath READ mousePath NOTIFY mousePathChanged) 0162 0163 public: 0164 SelectedItemWrapper(AnnotationDocument *document); 0165 ~SelectedItemWrapper(); 0166 0167 // The item we are selecting. 0168 HistoryItem::const_weak_ptr selectedItem() const; 0169 void setSelectedItem(const HistoryItem::const_shared_ptr &item); 0170 0171 // Transform the item with the given x and y deltas and at the specified edges. 0172 // Specifying no edges or all edges only translates. 0173 // We don't set things like scale directly because that would require more complex logic to be 0174 // written in various places in QML files. 0175 Q_INVOKABLE void transform(qreal dx, qreal dy, Qt::Edges edges = {}); 0176 0177 // Pushes the temporary item to history and sets the selected item as the temporary item parent. 0178 // Returns whether the commit actually happened. 0179 Q_INVOKABLE bool commitChanges(); 0180 0181 // Resets the selected item, temp item and options. 0182 // Returns the render area of the reset items. 0183 QRectF reset(); 0184 0185 bool hasSelection() const; 0186 0187 AnnotationTool::Options options() const; 0188 0189 int strokeWidth() const; 0190 void setStrokeWidth(int width); 0191 0192 QColor strokeColor() const; 0193 void setStrokeColor(const QColor &color); 0194 0195 QColor fillColor() const; 0196 void setFillColor(const QColor &color); 0197 0198 QFont font() const; 0199 void setFont(const QFont &font); 0200 0201 QColor fontColor() const; 0202 void setFontColor(const QColor &color); 0203 0204 int number() const; 0205 void setNumber(int number); 0206 0207 QString text() const; 0208 void setText(const QString &text); 0209 0210 bool hasShadow() const; 0211 void setShadow(bool shadow); 0212 0213 QPainterPath mousePath() const; 0214 0215 Q_SIGNALS: 0216 void strokeWidthChanged(); 0217 void strokeColorChanged(); 0218 void fillColorChanged(); 0219 void fontChanged(); 0220 void fontColorChanged(); 0221 void numberChanged(); 0222 void textChanged(); 0223 void shadowChanged(); 0224 void mousePathChanged(); 0225 0226 private: 0227 AnnotationTool::Options m_options; 0228 HistoryItem::const_weak_ptr m_selectedItem; 0229 AnnotationDocument *const m_document; 0230 }; 0231 0232 QDebug operator<<(QDebug debug, const SelectedItemWrapper *); 0233 0234 Q_DECLARE_OPERATORS_FOR_FLAGS(AnnotationDocument::ContinueOptions)