File indexing completed on 2025-02-02 04:26:10
0001 /* SPDX-FileCopyrightText: 2024 Noah Davis <noahadvs@gmail.com> 0002 * SPDX-License-Identifier: LGPL-2.0-or-later 0003 */ 0004 0005 #pragma once 0006 0007 #include <QBrush> 0008 #include <QFont> 0009 #include <QHash> 0010 #include <QPainter> 0011 #include <QPainterPath> 0012 #include <QPen> 0013 #include <QUuid> 0014 0015 #include <optional> 0016 #include <tuple> 0017 0018 namespace Traits 0019 { 0020 // Definitions all traits should have. 0021 // Sometimes clang-format formats macros poorly, so I'm disabling clang-format on macros. 0022 // clang-format off 0023 0024 #define COMMON_TRAIT_DEFS(ClassName)\ 0025 using Opt = std::optional<ClassName>;\ 0026 bool operator==(const ClassName &other) const = default; 0027 0028 // clang-format on 0029 0030 /* Not using QPixmap for images because I read from a Qt Company employee that QPixmaps are 0031 * almost always basically just QImages since Qt 5. 0032 * 0033 * https://www.reddit.com/r/QtFramework/comments/d9m17b/the_detailed_differences_between_qimage_and/f23m64t/ 0034 * 0035 * nezitcle (Qt Company): 0036 * 0037 * > So it may be important to point out, but in Qt 5 QPixmap is almost always a QImage. This was 0038 * > important in Qt 4 when we had multiple paint engines and then QPixmap was the "native" image 0039 * > format for the platform you were running on. There is only one case I can think of where 0040 * > QPixmap is not a QImage and that is when you are using the DirectFB platform plugin (which is 0041 * > probably busted at this point, and definitely legacy code). There QPixmap is a bona fide native 0042 * > surface (DFBSurface), but asside from that there is no real reason to use QPixmap over QImage 0043 * > in Qt 5, aside from signaling to the user of an API that they shouldn't modify the pixel data 0044 * > directly. 0045 */ 0046 0047 /** 0048 * Structs for each trait. 0049 * 0050 * These should all be kept pretty small and inexpensive to construct with minimal API. 0051 * 0052 * These should describe attributes of an abstract drawable object, not drawing tool object type. 0053 */ 0054 0055 struct Geometry { 0056 COMMON_TRAIT_DEFS(Geometry) 0057 // The base geometry path for any item that should be drawable. 0058 QPainterPath path{}; 0059 // The the path bounds used for mouse interaction. 0060 QPainterPath mousePath{}; 0061 // The rendered area, including all traits. 0062 // Adequate for rendering the associated item, 0063 // but not for overwriting the graphics of previous items. 0064 QRectF visualRect{}; 0065 }; 0066 0067 struct Stroke { 0068 COMMON_TRAIT_DEFS(Stroke) 0069 // The pen to use for Stroke::path. 0070 // Use pen.brush() with QPainter::setBrush instead of setPen when rendering Stroke::path. 0071 static QPen defaultPen(); 0072 QPen pen = defaultPen(); 0073 // Used for rendering, getting the visual bounds and mouse interaction areas. 0074 QPainterPath path{}; 0075 }; 0076 0077 namespace ImageEffects 0078 { 0079 struct Blur { 0080 // The factor by which original logical pixel sizes are multiplied. 0081 // One or less has no effect. 0082 uint factor = 1; 0083 Blur(uint factor); 0084 bool isValid() const; 0085 // Get an image that can be immediately used for rendering an image effect. 0086 // `getImage` should be the function used to generate the original image with no effects. 0087 // `rect` should be the section of the document you want to render over . 0088 // `dpr` should be the devicePixelRatio of the original image. 0089 QImage image(std::function<QImage()> getImage, QRectF rect, qreal dpr) const; 0090 bool operator==(const Blur &other) const = default; 0091 0092 protected: 0093 // Setting as mutable means it can be mutated even when this is const 0094 // or using a const member function. 0095 mutable QImage backingStoreCache{}; 0096 }; 0097 0098 struct Pixelate { 0099 // The factor by which original logical pixel sizes are multiplied. 0100 // One or less has no effect. 0101 uint factor = 1; 0102 Pixelate(uint factor); 0103 bool isValid() const; 0104 // Get an image that can be immediately used for rendering an image effect. 0105 // `getImage` should be the function used to generate the original image with no effects. 0106 // `rect` should be the section of the document you want to render over . 0107 // `dpr` should be the devicePixelRatio of the original image. 0108 QImage image(std::function<QImage()> getImage, QRectF rect, qreal dpr) const; 0109 bool operator==(const Pixelate &other) const = default; 0110 0111 protected: 0112 mutable QImage backingStoreCache{}; 0113 }; 0114 } 0115 0116 using FillVariant = std::variant<QBrush, ImageEffects::Blur, ImageEffects::Pixelate>; 0117 struct Fill : public FillVariant { 0118 COMMON_TRAIT_DEFS(Fill) 0119 enum Type : std::size_t { Brush, Blur, Pixelate, NPos = std::variant_npos }; 0120 }; 0121 0122 struct Highlight { 0123 COMMON_TRAIT_DEFS(Highlight) 0124 // The QPainter::CompositionMode to use for highlight effects 0125 static constexpr QPainter::CompositionMode compositionMode = QPainter::CompositionMode_Darken; 0126 }; 0127 0128 struct Arrow { 0129 COMMON_TRAIT_DEFS(Arrow) 0130 // TODO: support different styles 0131 enum Type { OneHead }; 0132 }; 0133 0134 // Set Geometry::path based on the bounding rect of this text. 0135 struct Text : public std::variant<QString, int> { 0136 COMMON_TRAIT_DEFS(Text) 0137 enum Type : std::size_t { String, Number, NPos = std::variant_npos }; 0138 int textFlags() const; 0139 // Set Geometry::path to the bounding rect of this text. 0140 QBrush brush{Qt::NoBrush}; 0141 QFont font{}; 0142 QString text() const; 0143 }; 0144 0145 // Scaled to fit Geometry::visualRect 0146 struct Shadow { 0147 COMMON_TRAIT_DEFS(Shadow) 0148 static constexpr int blurRadius = 2; 0149 static constexpr int xOffset = 2; 0150 static constexpr int yOffset = 2; 0151 static constexpr QMarginsF margins{ 0152 blurRadius + 1, 0153 blurRadius + 1, 0154 blurRadius + xOffset + 1, 0155 blurRadius + yOffset + 1, 0156 }; 0157 bool enabled = true; 0158 }; 0159 0160 // Group types 0161 template<typename... Args> 0162 struct ArgsType { 0163 using Tuple = std::tuple<Args...>; 0164 using OptTuple = std::tuple<typename Args::Opt...>; 0165 }; 0166 struct All : ArgsType<Geometry, Stroke, Fill, Highlight, Arrow, Text, Shadow> { 0167 }; 0168 using OptTuple = All::OptTuple; 0169 0170 struct Translation { 0171 // QTransform: m31 0172 // QMatrix4x4: 3,0 or m41 0173 qreal dx = 0; 0174 // QTransform: m32 0175 // QMatrix4x4: 3,1 or m42 0176 qreal dy = 0; 0177 }; 0178 0179 struct Scale { 0180 // QTransform: m11 0181 // QMatrix4x4: 0,0 or m11 0182 qreal sx = 1; 0183 // QTransform: m22 0184 // QMatrix4x4: 1,1 or m22 0185 qreal sy = 1; 0186 }; 0187 0188 // Undo a translation caused by scaling. 0189 // Scaling can also translate unless you apply an opposite translation. 0190 // `oldPoint` should be the position for the geometry you will apply the scale to. 0191 Translation unTranslateScale(qreal sx, qreal sy, const QPointF &oldPoint); 0192 0193 Scale scaleForSize(const QSizeF &oldSize, const QSizeF &newSize); 0194 0195 // The path, but with at least a tiny line to make it visible with a stroke when empty. 0196 QPainterPath minPath(const QPainterPath &path); 0197 0198 // Get an arrow head for the given line and stroke width. 0199 QPainterPath arrowHead(const QLineF &mainLine, qreal strokeWidth); 0200 0201 // Get the path based on the Text trait. 0202 QPainterPath createTextPath(const OptTuple &traits); 0203 0204 // Get the stroke path based on the available traits. 0205 QPainterPath createStrokePath(const OptTuple &traits); 0206 0207 // Constructs a mousePath based on the available traits. 0208 QPainterPath createMousePath(const OptTuple &traits); 0209 0210 // Constructs a visualRect based on the available traits. 0211 QRectF createVisualRect(const OptTuple &traits); 0212 0213 // Excludes computationally expensive parts that aren't needed for rendering 0214 void fastInitOptTuple(OptTuple &traits); 0215 0216 // Initialize an OptTuple the way a HistoryItem should have it. 0217 // Returns true if changes were done. 0218 void initOptTuple(OptTuple &traits); 0219 0220 // Clear the given traits in the OptTuple for reinitialization. 0221 void clearForInit(OptTuple &traits); 0222 0223 // clearForInit and initOptTuple 0224 void reInitTraits(OptTuple &traits); 0225 0226 // Apply a transformation to the traits of OptTuple, when possible. Mainly for translations. 0227 // If you want to scale a path, you typically don't want to scale the stroke width, so you should 0228 // typically not use this for scaling. Avoids copying whole paths when only translating. 0229 void transformTraits(const QTransform &transform, OptTuple &traits); 0230 0231 // Whether the values of the traits without std::optional are considered valid. 0232 template<typename T> 0233 bool isValidTrait(const T &trait); 0234 0235 // Whether the std::optionals are considered valid. 0236 template<typename T> 0237 bool isValidTraitOpt(const OptTuple &traits, bool isNullValid); 0238 0239 // Whether the traits are considered valid. 0240 // It is valid to not have any traits. 0241 bool isValid(const OptTuple &traits); 0242 0243 // Returns whether the set of traits can actually be seen by a user. 0244 bool isVisible(const OptTuple &traits); 0245 0246 // Returns the Geometry::mousePath or an empty path if not available. 0247 QPainterPath mousePath(const OptTuple &traits); 0248 0249 // Returns the Geometry::visualRect or an empty rect if not available. 0250 QRectF visualRect(const OptTuple &traits); 0251 0252 #undef COMMON_TRAIT_DEFS 0253 0254 } 0255 0256 // clang-format off 0257 #define DEBUG_DEF(ClassName)\ 0258 QDebug operator<<(QDebug debug, const ClassName &ref); 0259 // clang-format on 0260 0261 DEBUG_DEF(Traits::Geometry) 0262 DEBUG_DEF(Traits::Stroke) 0263 DEBUG_DEF(Traits::Fill) 0264 DEBUG_DEF(Traits::Highlight) 0265 DEBUG_DEF(Traits::Arrow) 0266 DEBUG_DEF(Traits::Text) 0267 DEBUG_DEF(Traits::Shadow) 0268 0269 DEBUG_DEF(Traits::ImageEffects::Blur) 0270 DEBUG_DEF(Traits::ImageEffects::Pixelate) 0271 0272 DEBUG_DEF(Traits::Geometry::Opt) 0273 DEBUG_DEF(Traits::Stroke::Opt) 0274 DEBUG_DEF(Traits::Fill::Opt) 0275 DEBUG_DEF(Traits::Highlight::Opt) 0276 DEBUG_DEF(Traits::Arrow::Opt) 0277 DEBUG_DEF(Traits::Text::Opt) 0278 DEBUG_DEF(Traits::Shadow::Opt) 0279 0280 DEBUG_DEF(Traits::OptTuple) 0281 0282 #undef DEBUG_DEF