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

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #ifndef __KIS_PAINT_DEVICE_DATA_H
0009 #define __KIS_PAINT_DEVICE_DATA_H
0010 
0011 #include "KisInterstrokeData.h"
0012 #include "KisSequentialIteratorProgress.h"
0013 #include "KoAlwaysInline.h"
0014 #include "kis_command_utils.h"
0015 #include "kundo2command.h"
0016 
0017 struct DirectDataAccessPolicy {
0018     DirectDataAccessPolicy(KisDataManager *dataManager, KisIteratorCompleteListener *completionListener)
0019         : m_dataManager(dataManager),
0020           m_completionListener(completionListener){}
0021 
0022 
0023     KisHLineConstIteratorSP createConstIterator(const QRect &rect) {
0024         const int xOffset = 0;
0025         const int yOffset = 0;
0026         return new KisHLineIterator2(m_dataManager, rect.x(), rect.y(), rect.width(), xOffset, yOffset, false, m_completionListener);
0027     }
0028 
0029     KisHLineIteratorSP createIterator(const QRect &rect) {
0030         const int xOffset = 0;
0031         const int yOffset = 0;
0032         return new KisHLineIterator2(m_dataManager, rect.x(), rect.y(), rect.width(), xOffset, yOffset, true, m_completionListener);
0033     }
0034 
0035     int pixelSize() const {
0036         return m_dataManager->pixelSize();
0037     }
0038 
0039     KisDataManager *m_dataManager;
0040     KisIteratorCompleteListener *m_completionListener;
0041 };
0042 
0043 class KisPaintDeviceData;
0044 
0045 class KisPaintDeviceData
0046 {
0047 public:
0048     KisPaintDeviceData(KisPaintDevice *paintDevice)
0049         : m_cache(paintDevice),
0050           m_x(0), m_y(0),
0051           m_colorSpace(0),
0052           m_levelOfDetail(0),
0053           m_cacheInvalidator(this)
0054         {
0055         }
0056 
0057     KisPaintDeviceData(KisPaintDevice *paintDevice, const KisPaintDeviceData *rhs, bool cloneContent)
0058         : m_dataManager(cloneContent ?
0059                         new KisDataManager(*rhs->m_dataManager) :
0060                         new KisDataManager(rhs->m_dataManager->pixelSize(), rhs->m_dataManager->defaultPixel())),
0061           m_cache(paintDevice),
0062           m_x(rhs->m_x),
0063           m_y(rhs->m_y),
0064           m_colorSpace(rhs->m_colorSpace),
0065           m_levelOfDetail(rhs->m_levelOfDetail),
0066           m_cacheInvalidator(this)
0067         {
0068             m_cache.setupCache();
0069             // WARNING: interstroke data is **not** copied while cloning, that is expected behavior!
0070         }
0071 
0072     void init(const KoColorSpace *cs, KisDataManagerSP dataManager) {
0073         m_colorSpace = cs;
0074         m_dataManager = dataManager;
0075         m_cache.setupCache();
0076     }
0077 
0078     class ChangeProfileCommand : public KUndo2Command {
0079     public:
0080         ChangeProfileCommand(KisPaintDeviceData *data,
0081                              const KoColorSpace *oldCs, const KoColorSpace *newCs,
0082                              KUndo2Command *parent)
0083             : KUndo2Command(parent),
0084               m_data(data),
0085               m_oldCs(oldCs),
0086               m_newCs(newCs)
0087         {
0088             Q_UNUSED(m_firstRun);
0089         }
0090 
0091         void redo() override {
0092             KUndo2Command::redo();
0093 
0094             m_data->m_colorSpace = m_newCs;
0095             m_data->m_cache.setupCache();
0096         }
0097 
0098         void undo() override {
0099             m_data->m_colorSpace = m_oldCs;
0100             m_data->m_cache.setupCache();
0101 
0102             KUndo2Command::undo();
0103         }
0104 
0105     protected:
0106         KisPaintDeviceData *m_data;
0107 
0108     private:
0109         bool m_firstRun {true};
0110         const KoColorSpace *m_oldCs;
0111         const KoColorSpace *m_newCs;
0112     };
0113 
0114 
0115     class ChangeColorSpaceCommand : public ChangeProfileCommand {
0116     public:
0117         ChangeColorSpaceCommand(KisPaintDeviceData *data,
0118                              KisDataManagerSP oldDm, KisDataManagerSP newDm,
0119                              const KoColorSpace *oldCs, const KoColorSpace *newCs,
0120                              KUndo2Command *parent)
0121             : ChangeProfileCommand(data, oldCs, newCs, parent),
0122               m_oldDm(oldDm),
0123               m_newDm(newDm)
0124         {
0125         }
0126 
0127         void redo() override {
0128             ChangeProfileCommand::redo();
0129             m_data->m_dataManager = m_newDm;
0130         }
0131 
0132         void undo() override {
0133             m_data->m_dataManager = m_oldDm;
0134             ChangeProfileCommand::undo();
0135         }
0136 
0137     private:
0138         KisDataManagerSP m_oldDm;
0139         KisDataManagerSP m_newDm;
0140     };
0141 
0142     void assignColorSpace(const KoColorSpace *dstColorSpace, KUndo2Command *parentCommand) {
0143         if (*m_colorSpace->profile() == *dstColorSpace->profile()) return;
0144 
0145         KIS_ASSERT_RECOVER_RETURN(m_colorSpace->pixelSize() == dstColorSpace->pixelSize());
0146 
0147         ChangeProfileCommand *cmd =
0148             new ChangeProfileCommand(this,
0149                                      m_colorSpace, dstColorSpace,
0150                                      parentCommand);
0151 
0152         // NOTE: first redo is skipped on a higher level,
0153         //       at DeviceChangeColorSpaceCommand
0154         cmd->redo();
0155 
0156         if (!parentCommand) {
0157             delete cmd;
0158         }
0159     }
0160 
0161     void convertDataColorSpace(const KoColorSpace *dstColorSpace,
0162                                KoColorConversionTransformation::Intent renderingIntent,
0163                                KoColorConversionTransformation::ConversionFlags conversionFlags,
0164                                KUndo2Command *parentCommand,
0165                                KoUpdater *updater = nullptr)
0166     {
0167         using InternalSequentialConstIterator =
0168             KisSequentialIteratorBase<ReadOnlyIteratorPolicy<DirectDataAccessPolicy>, DirectDataAccessPolicy, ProxyBasedProgressPolicy>;
0169         using InternalSequentialIterator =
0170             KisSequentialIteratorBase<WritableIteratorPolicy<DirectDataAccessPolicy>, DirectDataAccessPolicy, ProxyBasedProgressPolicy>;
0171 
0172         if (m_colorSpace == dstColorSpace || *m_colorSpace == *dstColorSpace) {
0173             return;
0174         }
0175 
0176         QRect rc = m_dataManager->region().boundingRect();
0177 
0178         const int dstPixelSize = dstColorSpace->pixelSize();
0179         QScopedArrayPointer<quint8> dstDefaultPixel(new quint8[dstPixelSize]);
0180         memset(dstDefaultPixel.data(), 0, dstPixelSize);
0181         m_colorSpace->convertPixelsTo(m_dataManager->defaultPixel(), dstDefaultPixel.data(), dstColorSpace, 1, renderingIntent, conversionFlags);
0182 
0183         KisDataManagerSP dstDataManager = new KisDataManager(dstPixelSize, dstDefaultPixel.data());
0184 
0185 
0186         if (!rc.isEmpty()) {
0187             InternalSequentialConstIterator srcIt(DirectDataAccessPolicy(m_dataManager.data(), cacheInvalidator()), rc, updater);
0188             InternalSequentialIterator dstIt(DirectDataAccessPolicy(dstDataManager.data(), cacheInvalidator()), rc, updater);
0189 
0190             int nConseqPixels = srcIt.nConseqPixels();
0191 
0192             // since we are accessing data managers directly, the columns are always aligned
0193             KIS_SAFE_ASSERT_RECOVER_NOOP(srcIt.nConseqPixels() == dstIt.nConseqPixels());
0194 
0195             while(srcIt.nextPixels(nConseqPixels) &&
0196                   dstIt.nextPixels(nConseqPixels)) {
0197 
0198                 nConseqPixels = srcIt.nConseqPixels();
0199 
0200                 const quint8 *srcData = srcIt.rawDataConst();
0201                 quint8 *dstData = dstIt.rawData();
0202 
0203                 m_colorSpace->convertPixelsTo(srcData, dstData,
0204                                               dstColorSpace,
0205                                               nConseqPixels,
0206                                               renderingIntent, conversionFlags);
0207             }
0208         }
0209 
0210         // becomes owned by the parent
0211         ChangeColorSpaceCommand *cmd =
0212             new ChangeColorSpaceCommand(this,
0213                                         m_dataManager, dstDataManager,
0214                                         m_colorSpace, dstColorSpace,
0215                                         parentCommand);
0216 
0217         // NOTE: first redo is skipped on a higher level,
0218         //       at DeviceChangeColorSpaceCommand
0219         cmd->redo();
0220 
0221         if (!parentCommand) {
0222             delete cmd;
0223         }
0224     }
0225 
0226     void reincarnateWithDetachedHistory(bool copyContent, KUndo2Command *parentCommand) {
0227         struct SwitchDataManager : public KUndo2Command
0228         {
0229             SwitchDataManager(KisPaintDeviceData *data,
0230                               KisDataManagerSP oldDm, KisDataManagerSP newDm,
0231                               KUndo2Command *parent = 0)
0232                 : KUndo2Command(parent),
0233                   m_data(data),
0234                   m_oldDm(oldDm),
0235                   m_newDm(newDm)
0236             {
0237             }
0238 
0239             void redo() override {
0240                 m_data->m_dataManager = m_newDm;
0241                 m_data->cache()->invalidate();
0242             }
0243 
0244             void undo() override {
0245                 m_data->m_dataManager = m_oldDm;
0246                 m_data->cache()->invalidate();
0247             }
0248 
0249         private:
0250             KisPaintDeviceData *m_data;
0251             KisDataManagerSP m_oldDm;
0252             KisDataManagerSP m_newDm;
0253         };
0254 
0255         new KisCommandUtils::LambdaCommand(parentCommand,
0256             [this, copyContent] () {
0257                 KisDataManagerSP newDm =
0258                     copyContent ?
0259                     new KisDataManager(*this->dataManager()) :
0260                     new KisDataManager(this->dataManager()->pixelSize(), this->dataManager()->defaultPixel());
0261                 return new SwitchDataManager(this, this->dataManager(), newDm);
0262             });
0263     }
0264 
0265     void prepareClone(const KisPaintDeviceData *srcData, bool copyContent = false) {
0266         m_x = srcData->x();
0267         m_y = srcData->y();
0268 
0269         if (copyContent) {
0270             m_dataManager = new KisDataManager(*srcData->dataManager());
0271         } else if (m_dataManager->pixelSize() !=
0272                    srcData->dataManager()->pixelSize()) {
0273             // NOTE: we don't check default pixel value! it is the task of
0274             //       the higher level!
0275 
0276             m_dataManager = new KisDataManager(srcData->dataManager()->pixelSize(), srcData->dataManager()->defaultPixel());
0277             m_cache.setupCache();
0278         } else {
0279             m_dataManager->clear();
0280             const quint8 *srcDefPixel = srcData->dataManager()->defaultPixel();
0281 
0282             const int cmp =
0283                 memcmp(srcDefPixel,
0284                        m_dataManager->defaultPixel(),
0285                        m_dataManager->pixelSize());
0286 
0287             if (cmp != 0) {
0288                 m_dataManager->setDefaultPixel(srcDefPixel);
0289             }
0290         }
0291 
0292         m_levelOfDetail = srcData->levelOfDetail();
0293         m_colorSpace = srcData->colorSpace();
0294         m_cache.invalidate();
0295     }
0296 
0297     ALWAYS_INLINE KisDataManagerSP dataManager() const {
0298         return m_dataManager;
0299     }
0300 
0301     ALWAYS_INLINE KisPaintDeviceCache* cache() {
0302         return &m_cache;
0303     }
0304 
0305     ALWAYS_INLINE qint32 x() const {
0306         return m_x;
0307     }
0308     ALWAYS_INLINE void setX(qint32 value) {
0309         m_x = value;
0310     }
0311 
0312     ALWAYS_INLINE qint32 y() const {
0313         return m_y;
0314     }
0315     ALWAYS_INLINE void setY(qint32 value) {
0316         m_y = value;
0317     }
0318 
0319     ALWAYS_INLINE const KoColorSpace* colorSpace() const {
0320         return m_colorSpace;
0321     }
0322 
0323     ALWAYS_INLINE qint32 levelOfDetail() const {
0324         return m_levelOfDetail;
0325     }
0326     ALWAYS_INLINE void setLevelOfDetail(qint32 value) {
0327         m_levelOfDetail = value;
0328     }
0329 
0330     ALWAYS_INLINE KisIteratorCompleteListener* cacheInvalidator() {
0331         return &m_cacheInvalidator;
0332     }
0333 
0334     ALWAYS_INLINE KisInterstrokeDataSP interstrokeData() const {
0335         return m_interstrokeData;
0336     }
0337 
0338     ALWAYS_INLINE KUndo2Command* createChangeInterstrokeDataCommand(KisInterstrokeDataSP value) {
0339         struct SwapInterstrokeDataCommand : public KUndo2Command
0340         {
0341             SwapInterstrokeDataCommand(KisPaintDeviceData *q, KisInterstrokeDataSP data)
0342                 : m_q(q),
0343                   m_data(data)
0344             {
0345             }
0346 
0347             void redo() override {
0348                 std::swap(m_data, m_q->m_interstrokeData);
0349             }
0350 
0351             void undo() override {
0352                 std::swap(m_data, m_q->m_interstrokeData);
0353             }
0354 
0355         private:
0356             KisPaintDeviceData *m_q;
0357             KisInterstrokeDataSP m_data;
0358         };
0359 
0360         return new SwapInterstrokeDataCommand(this, value);
0361     }
0362 
0363 
0364 private:
0365     struct CacheInvalidator : public KisIteratorCompleteListener {
0366         CacheInvalidator(KisPaintDeviceData *_q) : q(_q) {}
0367 
0368         void notifyWritableIteratorCompleted() override {
0369             q->cache()->invalidate();
0370         }
0371     private:
0372         KisPaintDeviceData *q;
0373     };
0374 
0375 
0376 private:
0377 
0378     KisDataManagerSP m_dataManager;
0379     KisPaintDeviceCache m_cache;
0380     qint32 m_x;
0381     qint32 m_y;
0382     const KoColorSpace* m_colorSpace;
0383     qint32 m_levelOfDetail;
0384     CacheInvalidator m_cacheInvalidator;
0385     KisInterstrokeDataSP m_interstrokeData;
0386 };
0387 
0388 #endif /* __KIS_PAINT_DEVICE_DATA_H */