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