File indexing completed on 2024-05-12 15:58:29
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_onion_skin_cache.h" 0008 0009 #include <QReadWriteLock> 0010 #include <QReadLocker> 0011 #include <QWriteLocker> 0012 0013 0014 #include "kis_paint_device.h" 0015 #include "kis_onion_skin_compositor.h" 0016 #include "kis_default_bounds.h" 0017 #include "kis_time_span.h" 0018 #include "kis_image.h" 0019 #include "KoColorSpace.h" 0020 0021 #include "kis_raster_keyframe_channel.h" 0022 0023 0024 struct KisOnionSkinCache::Private 0025 { 0026 KisPaintDeviceSP cachedProjection; 0027 0028 int cacheTime = 0; 0029 int cacheConfigSeqNo = 0; 0030 int framesHash = 0; 0031 QReadWriteLock lock; 0032 0033 bool checkCacheValid(KisPaintDeviceSP source, KisOnionSkinCompositor *compositor) { 0034 const KisRasterKeyframeChannel *keyframes = source->keyframeChannel(); 0035 0036 const int time = source->defaultBounds()->currentTime(); 0037 const KisTimeSpan span = source->keyframeChannel()->identicalFrames(cacheTime); 0038 const int seqNo = compositor->configSeqNo(); 0039 const int hash = keyframes->channelHash(); 0040 0041 return span.contains(time) && cacheConfigSeqNo == seqNo && framesHash == hash; 0042 } 0043 0044 void updateCacheMetrics(KisPaintDeviceSP source, KisOnionSkinCompositor *compositor) { 0045 const KisRasterKeyframeChannel *keyframes = source->keyframeChannel(); 0046 0047 const int time = source->defaultBounds()->currentTime(); 0048 const int seqNo = compositor->configSeqNo(); 0049 const int hash = keyframes->channelHash(); 0050 0051 cacheTime = time; 0052 cacheConfigSeqNo = seqNo; 0053 framesHash = hash; 0054 } 0055 }; 0056 0057 KisOnionSkinCache::KisOnionSkinCache() 0058 : m_d(new Private) 0059 { 0060 } 0061 0062 KisOnionSkinCache::~KisOnionSkinCache() 0063 { 0064 } 0065 0066 KisPaintDeviceSP KisOnionSkinCache::projection(KisPaintDeviceSP source) 0067 { 0068 KisOnionSkinCompositor *compositor = KisOnionSkinCompositor::instance(); 0069 0070 KisPaintDeviceSP cachedProjection; 0071 0072 QReadLocker readLocker(&m_d->lock); 0073 cachedProjection = m_d->cachedProjection; 0074 0075 0076 if (!cachedProjection || !m_d->checkCacheValid(source, compositor)) { 0077 0078 readLocker.unlock(); 0079 QWriteLocker writeLocker(&m_d->lock); 0080 cachedProjection = m_d->cachedProjection; 0081 if (!cachedProjection || 0082 !m_d->checkCacheValid(source, compositor) || 0083 *cachedProjection->colorSpace() != *source->colorSpace()) { 0084 0085 if (!cachedProjection) { 0086 cachedProjection = new KisPaintDevice(source->colorSpace()); 0087 } else { 0088 cachedProjection->setDefaultBounds(new KisDefaultBounds()); 0089 cachedProjection->clear(); 0090 0091 if (*cachedProjection->colorSpace() != *source->colorSpace()) { 0092 cachedProjection->convertTo(source->colorSpace()); 0093 } 0094 } 0095 0096 const QRect extent = compositor->calculateExtent(source); 0097 compositor->composite(source, cachedProjection, extent); 0098 0099 cachedProjection->setDefaultBounds(source->defaultBounds()); 0100 0101 /** 0102 * It might happen that the lod planes has already been 0103 * generated for all the devices, so we should cold-init them 0104 * for the onion skins. 0105 */ 0106 const int lod = source->defaultBounds()->currentLevelOfDetail(); 0107 if (lod > 0) { 0108 QScopedPointer<KisPaintDevice::LodDataStruct> data(cachedProjection->createLodDataStruct(lod)); 0109 cachedProjection->updateLodDataStruct(data.data(), extent); 0110 cachedProjection->uploadLodDataStruct(data.data()); 0111 } 0112 0113 m_d->updateCacheMetrics(source, compositor); 0114 m_d->cachedProjection = cachedProjection; 0115 } 0116 } 0117 0118 return cachedProjection; 0119 } 0120 0121 void KisOnionSkinCache::reset() 0122 { 0123 QWriteLocker writeLocker(&m_d->lock); 0124 m_d->cachedProjection = 0; 0125 } 0126 0127 KisPaintDeviceSP KisOnionSkinCache::lodCapableDevice() const 0128 { 0129 return m_d->cachedProjection; 0130 }