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 */