File indexing completed on 2024-05-12 15:58:52
0001 /* 0002 * SPDX-FileCopyrightText: 2019 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KisSafeNodeProjectionStore.h" 0008 0009 #include <QCoreApplication> 0010 #include <QVector> 0011 #include <KoColorSpace.h> 0012 0013 #include "kis_image.h" 0014 #include "kis_paint_device.h" 0015 #include "kis_selection.h" 0016 #include "KisRecycleProjectionsJob.h" 0017 0018 /**********************************************************************/ 0019 /* StoreImplementaionInterface */ 0020 /**********************************************************************/ 0021 0022 struct StoreImplementaionInterface 0023 { 0024 virtual ~StoreImplementaionInterface() {} 0025 virtual StoreImplementaionInterface* clone() const = 0; 0026 virtual bool releaseDevice() = 0; 0027 virtual void discardCaches() = 0; 0028 virtual void recycleProjectionsInSafety() = 0; 0029 }; 0030 0031 0032 /**********************************************************************/ 0033 /* StoreImplementaion */ 0034 /**********************************************************************/ 0035 0036 template <typename DeviceSP> 0037 struct StoreImplementation : public StoreImplementaionInterface 0038 { 0039 bool releaseDevice() override { 0040 bool hasDeletedProjection = false; 0041 0042 if (m_projection) { 0043 m_dirtyProjections.append(m_projection); 0044 m_projection = 0; 0045 hasDeletedProjection = true; 0046 0047 // qDebug() << "release a device"; 0048 } 0049 return hasDeletedProjection; 0050 } 0051 0052 virtual void discardCaches() override { 0053 // qDebug() << "discard caches"; 0054 m_dirtyProjections.clear(); 0055 } 0056 0057 virtual void recycleProjectionsInSafety() override { 0058 // qDebug() << "recycle caches"; 0059 Q_FOREACH (DeviceSP projection, m_dirtyProjections) { 0060 projection->clear(); 0061 m_cleanProjections.append(projection); 0062 } 0063 m_dirtyProjections.clear(); 0064 } 0065 0066 protected: 0067 DeviceSP m_projection; 0068 QVector<DeviceSP> m_dirtyProjections; 0069 QVector<DeviceSP> m_cleanProjections; 0070 }; 0071 0072 0073 /**********************************************************************/ 0074 /* StoreImplementaionForDevice */ 0075 /**********************************************************************/ 0076 0077 struct StoreImplementationForDevice : StoreImplementation<KisPaintDeviceSP> 0078 { 0079 StoreImplementationForDevice() {} 0080 StoreImplementationForDevice(const KisPaintDevice &prototype) { 0081 m_projection = new KisPaintDevice(prototype); 0082 } 0083 0084 StoreImplementaionInterface* clone() const override { 0085 return m_projection ? 0086 new StoreImplementationForDevice(*m_projection) : 0087 new StoreImplementationForDevice(); 0088 } 0089 0090 KisPaintDeviceSP getDeviceLazy(KisPaintDeviceSP prototype) { 0091 if(!m_projection || 0092 *m_projection->colorSpace() != *prototype->colorSpace()) { 0093 0094 if (!m_cleanProjections.isEmpty()) { 0095 m_projection = m_cleanProjections.takeLast(); 0096 m_projection->makeCloneFromRough(prototype, prototype->extent()); 0097 } else { 0098 m_projection = new KisPaintDevice(*prototype); 0099 } 0100 0101 m_projection->setProjectionDevice(true); 0102 } 0103 return m_projection; 0104 } 0105 }; 0106 0107 0108 /**********************************************************************/ 0109 /* StoreImplementaionForSelection */ 0110 /**********************************************************************/ 0111 0112 struct StoreImplementationForSelection : StoreImplementation<KisSelectionSP> 0113 { 0114 StoreImplementationForSelection() {} 0115 StoreImplementationForSelection(const KisSelection &prototype) { 0116 m_projection = new KisSelection(prototype); 0117 } 0118 0119 StoreImplementaionInterface* clone() const override { 0120 return m_projection ? 0121 new StoreImplementationForSelection(*m_projection) : 0122 new StoreImplementationForSelection(); 0123 } 0124 0125 KisSelectionSP getDeviceLazy(KisSelectionSP prototype) { 0126 if(!m_projection) { 0127 if (!m_cleanProjections.isEmpty()) { 0128 m_projection = m_cleanProjections.takeLast(); 0129 m_projection->pixelSelection()->makeCloneFromRough(prototype->pixelSelection(), prototype->selectedRect()); 0130 } else { 0131 m_projection = new KisSelection(*prototype); 0132 } 0133 0134 m_projection->pixelSelection()->setProjectionDevice(true); 0135 } 0136 return m_projection; 0137 } 0138 }; 0139 0140 0141 /**********************************************************************/ 0142 /* KisSafeNodeProjectionStoreBase */ 0143 /**********************************************************************/ 0144 0145 struct KisSafeNodeProjectionStoreBase::Private 0146 { 0147 mutable QMutex lock; 0148 KisImageWSP image; 0149 QScopedPointer<StoreImplementaionInterface> store; 0150 }; 0151 0152 KisSafeNodeProjectionStoreBase::KisSafeNodeProjectionStoreBase(StoreImplementaionInterface *storeImpl) 0153 : m_d(new Private) 0154 { 0155 m_d->store.reset(storeImpl); 0156 moveToThread(qApp->thread()); 0157 connect(this, SIGNAL(internalInitiateProjectionsCleanup()), this, SLOT(slotInitiateProjectionsCleanup())); 0158 } 0159 0160 KisSafeNodeProjectionStoreBase::KisSafeNodeProjectionStoreBase(const KisSafeNodeProjectionStoreBase &rhs) 0161 : QObject(), 0162 KisShared(), 0163 m_d(new Private) 0164 { 0165 { 0166 QMutexLocker rhsLocker(&rhs.m_d->lock); 0167 0168 m_d->image = rhs.m_d->image; 0169 m_d->store.reset(rhs.m_d->store->clone()); 0170 } 0171 0172 moveToThread(qApp->thread()); 0173 connect(this, SIGNAL(internalInitiateProjectionsCleanup()), this, SLOT(slotInitiateProjectionsCleanup())); 0174 } 0175 0176 KisSafeNodeProjectionStoreBase::~KisSafeNodeProjectionStoreBase() 0177 { 0178 } 0179 0180 void KisSafeNodeProjectionStoreBase::releaseDevice() 0181 { 0182 QMutexLocker locker(&m_d->lock); 0183 if (m_d->store->releaseDevice()) { 0184 locker.unlock(); 0185 emit internalInitiateProjectionsCleanup(); 0186 } 0187 } 0188 0189 void KisSafeNodeProjectionStoreBase::setImage(KisImageWSP image) 0190 { 0191 m_d->image = image; 0192 } 0193 0194 void KisSafeNodeProjectionStoreBase::slotInitiateProjectionsCleanup() 0195 { 0196 /** 0197 * After the projection has been used, we should clean it. But we cannot 0198 * clean it until all the workers accessing it have completed their job. 0199 * 0200 * Therefore we just schedule an exclusive job that will execute the 0201 * recycling action in an exclusive context, when no jobs are running. 0202 */ 0203 0204 KisImageSP image = m_d->image; 0205 0206 if (image) { 0207 image->addSpontaneousJob(new KisRecycleProjectionsJob(this)); 0208 } else { 0209 discardCaches(); 0210 } 0211 } 0212 0213 void KisSafeNodeProjectionStoreBase::discardCaches() 0214 { 0215 QMutexLocker locker(&m_d->lock); 0216 m_d->store->discardCaches(); 0217 } 0218 0219 void KisSafeNodeProjectionStoreBase::recycleProjectionsInSafety() 0220 { 0221 QMutexLocker locker(&m_d->lock); 0222 m_d->store->recycleProjectionsInSafety(); 0223 } 0224 0225 0226 /**********************************************************************/ 0227 /* KisSafeNodeProjectionStore */ 0228 /**********************************************************************/ 0229 0230 KisSafeNodeProjectionStore::KisSafeNodeProjectionStore() 0231 : KisSafeNodeProjectionStoreBase(new StoreImplementationForDevice) 0232 { 0233 } 0234 0235 KisSafeNodeProjectionStore::KisSafeNodeProjectionStore(const KisSafeNodeProjectionStore &rhs) 0236 : KisSafeNodeProjectionStoreBase(rhs) 0237 { 0238 } 0239 0240 KisPaintDeviceSP KisSafeNodeProjectionStore::getDeviceLazy(KisPaintDeviceSP prototype) 0241 { 0242 QMutexLocker locker(&m_d->lock); 0243 StoreImplementationForDevice *store = dynamic_cast<StoreImplementationForDevice*>(m_d->store.data()); 0244 KIS_ASSERT(store); 0245 0246 return store->getDeviceLazy(prototype); 0247 } 0248 0249 0250 /**********************************************************************/ 0251 /* KisSafeSelectionNodeProjectionStore */ 0252 /**********************************************************************/ 0253 0254 KisSafeSelectionNodeProjectionStore::KisSafeSelectionNodeProjectionStore() 0255 : KisSafeNodeProjectionStoreBase(new StoreImplementationForSelection) 0256 { 0257 } 0258 0259 KisSafeSelectionNodeProjectionStore::KisSafeSelectionNodeProjectionStore(const KisSafeSelectionNodeProjectionStore &rhs) 0260 : KisSafeNodeProjectionStoreBase(rhs) 0261 { 0262 } 0263 0264 KisSelectionSP KisSafeSelectionNodeProjectionStore::getDeviceLazy(KisSelectionSP prototype) 0265 { 0266 QMutexLocker locker(&m_d->lock); 0267 StoreImplementationForSelection *store = dynamic_cast<StoreImplementationForSelection*>(m_d->store.data()); 0268 KIS_ASSERT(store); 0269 0270 return store->getDeviceLazy(prototype); 0271 } 0272