File indexing completed on 2024-05-19 04:26:25

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef __KIS_PAINT_DEVICE_CACHE_H
0008 #define __KIS_PAINT_DEVICE_CACHE_H
0009 
0010 #include "kis_lock_free_cache.h"
0011 #include <QElapsedTimer>
0012 #include <QReadWriteLock>
0013 #include <QReadLocker>
0014 #include <QWriteLocker>
0015 
0016 class KisPaintDeviceCache
0017 {
0018 public:
0019     KisPaintDeviceCache(KisPaintDevice *paintDevice)
0020         : m_paintDevice(paintDevice),
0021           m_exactBoundsCache(paintDevice),
0022           m_nonDefaultPixelAreaCache(paintDevice),
0023           m_regionCache(paintDevice),
0024           m_sequenceNumber(0)
0025     {
0026     }
0027 
0028     KisPaintDeviceCache(const KisPaintDeviceCache &rhs)
0029         : m_paintDevice(rhs.m_paintDevice),
0030           m_exactBoundsCache(rhs.m_paintDevice),
0031           m_nonDefaultPixelAreaCache(rhs.m_paintDevice),
0032           m_regionCache(rhs.m_paintDevice),
0033           m_sequenceNumber(0)
0034     {
0035     }
0036 
0037     void setupCache() {
0038         invalidate();
0039     }
0040 
0041     void invalidate() {
0042         m_thumbnailsValid = false;
0043         m_exactBoundsCache.invalidate();
0044         m_nonDefaultPixelAreaCache.invalidate();
0045         m_regionCache.invalidate();
0046         m_sequenceNumber++;
0047     }
0048 
0049     QRect exactBounds() {
0050         return m_exactBoundsCache.getValue(m_paintDevice->defaultBounds()->wrapAroundMode());
0051     }
0052 
0053     QRect exactBoundsAmortized() {
0054         QRect bounds;
0055         bool result = m_exactBoundsCache.tryGetValue(bounds, m_paintDevice->defaultBounds()->wrapAroundMode());
0056 
0057         if (!result) {
0058             /**
0059              * The calculation of the exact bounds might be too slow
0060              * in some special cases, e.g. for an empty canvas of 7k
0061              * by 6k.  So we just always return extent, when the exact
0062              * bounds is not available.
0063              */
0064             bounds = m_paintDevice->extent();
0065         }
0066 
0067         return bounds;
0068     }
0069 
0070     QRect nonDefaultPixelArea() {
0071         return m_nonDefaultPixelAreaCache.getValue(m_paintDevice->defaultBounds()->wrapAroundMode());
0072     }
0073 
0074     KisRegion region() {
0075         return m_regionCache.getValue(m_paintDevice->defaultBounds()->wrapAroundMode());
0076     }
0077 
0078     QImage createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) {
0079         QImage thumbnail;
0080 
0081         if (h == 0 || w == 0) {
0082             return thumbnail;
0083         }
0084 
0085         {
0086             QReadLocker readLocker(&m_thumbnailsLock);
0087             if (m_thumbnailsValid) {
0088                 if (m_thumbnails.contains(w) && m_thumbnails[w].contains(h) && m_thumbnails[w][h].contains(oversample)) {
0089                     thumbnail = m_thumbnails[w][h][oversample];
0090                 }
0091             }
0092             else {
0093                 readLocker.unlock();
0094                 QWriteLocker writeLocker(&m_thumbnailsLock);
0095                 m_thumbnails.clear();
0096                 m_thumbnailsValid = true;
0097             }
0098         }
0099 
0100         if (thumbnail.isNull()) {
0101             // the thumbnails in the cache are always generated from exact bounds
0102             thumbnail = m_paintDevice->createThumbnail(w, h, m_paintDevice->exactBounds(), oversample, renderingIntent, conversionFlags);
0103 
0104             QWriteLocker writeLocker(&m_thumbnailsLock);
0105             m_thumbnails[w][h][oversample] = thumbnail;
0106             m_thumbnailsValid = true;
0107         }
0108 
0109         return thumbnail;
0110     }
0111 
0112     int sequenceNumber() const {
0113         return m_sequenceNumber;
0114     }
0115 
0116 private:
0117     KisPaintDevice *m_paintDevice {nullptr};
0118 
0119     struct ExactBoundsCache : KisLockFreeCacheWithModeConsistency<QRect, bool> {
0120         ExactBoundsCache(KisPaintDevice *paintDevice) : m_paintDevice(paintDevice) {}
0121 
0122         QRect calculateNewValue() const override {
0123             return m_paintDevice->calculateExactBounds(false);
0124         }
0125     private:
0126         KisPaintDevice *m_paintDevice;
0127     };
0128 
0129     struct NonDefaultPixelCache : KisLockFreeCacheWithModeConsistency<QRect, bool> {
0130         NonDefaultPixelCache(KisPaintDevice *paintDevice) : m_paintDevice(paintDevice) {}
0131 
0132         QRect calculateNewValue() const override {
0133             return m_paintDevice->calculateExactBounds(true);
0134         }
0135     private:
0136         KisPaintDevice *m_paintDevice;
0137     };
0138 
0139     struct RegionCache : KisLockFreeCacheWithModeConsistency<KisRegion, bool> {
0140         RegionCache(KisPaintDevice *paintDevice) : m_paintDevice(paintDevice) {}
0141 
0142         KisRegion calculateNewValue() const override {
0143             return m_paintDevice->dataManager()->region();
0144         }
0145     private:
0146         KisPaintDevice *m_paintDevice;
0147     };
0148 
0149     ExactBoundsCache m_exactBoundsCache;
0150     NonDefaultPixelCache m_nonDefaultPixelAreaCache;
0151     RegionCache m_regionCache;
0152 
0153     QReadWriteLock m_thumbnailsLock;
0154     bool m_thumbnailsValid {false};
0155     QMap<int, QMap<int, QMap<qreal,QImage> > > m_thumbnails;
0156 
0157     QAtomicInt m_sequenceNumber;
0158 };
0159 
0160 #endif /* __KIS_PAINT_DEVICE_CACHE_H */