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