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 }