File indexing completed on 2024-05-12 15:58:52

0001 /*
0002  *  SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef KISOVERLAYPAINTDEVICEWRAPPER_H
0008 #define KISOVERLAYPAINTDEVICEWRAPPER_H
0009 
0010 #include <kis_types.h>
0011 #include "kritaimage_export.h"
0012 
0013 class KoColorSpace;
0014 
0015 
0016 /**
0017  * A special wrapper class for a paint device that allows working with parts of
0018  * the source paint device using an overlay, that is without modifying the
0019  * device itself. The overlay may have higher bit depth if PreciseMode or
0020  * LazyPreciseMode is used.
0021  *
0022  * For example, you have an RGBA8 paint device, but you want all the blending
0023  * happen in higher bit depth. You wrap your paint device (source paint device)
0024  * into KisOverlayPaintDeviceWrapper, and the wrapper creates a temporary
0025  * device (precise overlay paint device) in RGBA16 colorspace. Then you work
0026  * with this precise paint device as usual, uploading and downloading pixel
0027  * data to/from the source paint device using readRect() and writeRect()
0028  *
0029  * In LazyPreciseMode, if the source device is already "precise", that is
0030  * having the bit depth higher than 8 bit per channel, no temporary device is
0031  * created. All the operations are forwarded directly to the source device.
0032  *
0033  * In some cases (e.g. when trying to maintain a separate heightmap channel),
0034  * you may want to have multiple overlays.
0035  *
0036  * When doing conversions between U8 and U16 color spaces, the overlay
0037  * will try to use AVX2-optimized algorithms.
0038  *
0039  * Example:
0040  *
0041  * \code{.cpp}
0042  * // initialize the wrapper with the source device,
0043  * // it creates a precise device if needed
0044  * KisOverlayPaintDeviceWrapper wrapper(sourceDevice, 1, KisOverlayPaintDeviceWrapper::LazyPreciseMode);
0045  *
0046  * // Download the data from the source device. The rects
0047  * // that have already been read will be cached and will
0048  * // never be read twice.
0049  * wrapper.readRect(accessRect);
0050  *
0051  * // start modifying the data
0052  * KisPainter gc(wrapper.overlay());
0053  *
0054  * // low opacity might be handled incorrectly in the original
0055  * // color space, but we work in a precise one!
0056  * gc.setOpacity(1);
0057  * gc.bitBlt(accessRect.topLeft(), someOtherDevice, accessRect);
0058  *
0059  * // ... repeat multiple times if needed ...
0060  *
0061  * // upload the data back to the original source device
0062  * wrapper.writeRect(accessRect);
0063  * \endcode
0064  *
0065  */
0066 
0067 class KRITAIMAGE_EXPORT KisOverlayPaintDeviceWrapper
0068 {
0069 public:
0070     enum OverlayMode {
0071         NormalMode = 0,
0072         PreciseMode,
0073         LazyPreciseMode
0074     };
0075 
0076 public:
0077     /**
0078      * Create an overlay wrapper and attach it to \p device.
0079      *
0080      * If \p mode is `NormalMode`, then \p numOverlays are created. All overlays
0081      * will have the same (possible "imprecise") colorspace as \p source.
0082      *
0083      * If \p mode is `PreciseMode`, then \p numOverlays are created. If \p source
0084      * has "imprecise" color space (U8), then overlays will be upgraded to a
0085      * "precise" color space (U16).
0086      *
0087      * If \p mode is `LazyPreciseMode` **and** \p numOverlays is 1, then the
0088      * overlay will be created only in case when precise color space is
0089      * needed. Otherwise, the mode behaves like `PreciseMode`
0090      *
0091      * \param device source device
0092      * \param numOverlays the number of overlays to create
0093      * \param mode mode to use
0094      * \param forcedOverlayColorSpace forced color space to use for overlay. Passing non-
0095      *        null as forcedOverlayColorSpace will override precise color space decision
0096      *        process. This argument is useful for cases when two overlay devices need to
0097      *        have exactly the same color space (e.g. in colorsmudge overlay mode).
0098      */
0099     KisOverlayPaintDeviceWrapper(KisPaintDeviceSP source, int numOverlays = 1, OverlayMode mode = NormalMode, const KoColorSpace *forcedOverlayColorSpace = nullptr);
0100 
0101     ~KisOverlayPaintDeviceWrapper();
0102 
0103     void setExternalDestination(KisPaintDeviceSP device);
0104     KisPaintDeviceSP externalDestination() const;
0105 
0106     KisPaintDeviceSP source() const;
0107     KisPaintDeviceSP overlay(int index = 0) const;
0108 
0109     void readRect(const QRect &rc);
0110     void writeRect(const QRect &rc, int index = 0);
0111 
0112     void readRects(const QVector<QRect> &rects);
0113     void writeRects(const QVector<QRect> &rects, int index = 0);
0114 
0115     const KoColorSpace* overlayColorSpace() const;
0116 
0117     /**
0118      * Create a composite source device for being used over overlay().
0119      *
0120      * Please note thate one cannot use
0121      * overlay()->createCompositeSourceDevice() for this purpose because
0122      * overlay() is just a copy of sourceDevice() and doesn't have overloaded
0123      * methods for this color space.
0124      *
0125      * TODO: make KisPaintDevice::compositeSourceColorSpace() not a virtual method,
0126      *       but let is be assigned during the lifetime of the paint device. It'll
0127      *       let us remove this extra function.
0128      */
0129     KisPaintDeviceSP createPreciseCompositionSourceDevice();
0130 
0131     void beginTransaction(KUndo2Command *parent = 0);
0132     KUndo2Command *endTransaction();
0133 
0134 private:
0135 
0136     friend struct KisChangeOverlayWrapperCommand;
0137     struct Private;
0138     const QScopedPointer<Private> m_d;
0139 };
0140 
0141 #endif // KISOVERLAYPAINTDEVICEWRAPPER_H