File indexing completed on 2024-05-12 15:58:31

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 
0013 
0014 class KisPaintDeviceCache
0015 {
0016 public:
0017     KisPaintDeviceCache(KisPaintDevice *paintDevice)
0018         : m_paintDevice(paintDevice),
0019           m_exactBoundsCache(paintDevice),
0020           m_nonDefaultPixelAreaCache(paintDevice),
0021           m_regionCache(paintDevice),
0022           m_sequenceNumber(0)
0023     {
0024     }
0025 
0026     KisPaintDeviceCache(const KisPaintDeviceCache &rhs)
0027         : m_paintDevice(rhs.m_paintDevice),
0028           m_exactBoundsCache(rhs.m_paintDevice),
0029           m_nonDefaultPixelAreaCache(rhs.m_paintDevice),
0030           m_regionCache(rhs.m_paintDevice),
0031           m_sequenceNumber(0)
0032     {
0033     }
0034 
0035     void setupCache() {
0036         invalidate();
0037     }
0038 
0039     void invalidate() {
0040         m_thumbnailsValid = false;
0041         m_exactBoundsCache.invalidate();
0042         m_nonDefaultPixelAreaCache.invalidate();
0043         m_regionCache.invalidate();
0044         m_sequenceNumber++;
0045     }
0046 
0047     QRect exactBounds() {
0048         return m_exactBoundsCache.getValue(m_paintDevice->defaultBounds()->wrapAroundMode());
0049     }
0050 
0051     QRect exactBoundsAmortized() {
0052         QRect bounds;
0053         bool result = m_exactBoundsCache.tryGetValue(bounds, m_paintDevice->defaultBounds()->wrapAroundMode());
0054 
0055         if (!result) {
0056             /**
0057              * The calculation of the exact bounds might be too slow
0058              * in some special cases, e.g. for an empty canvas of 7k
0059              * by 6k.  So we just always return extent, when the exact
0060              * bounds is not available.
0061              */
0062             bounds = m_paintDevice->extent();
0063         }
0064 
0065         return bounds;
0066     }
0067 
0068     QRect nonDefaultPixelArea() {
0069         return m_nonDefaultPixelAreaCache.getValue(m_paintDevice->defaultBounds()->wrapAroundMode());
0070     }
0071 
0072     KisRegion region() {
0073         return m_regionCache.getValue(m_paintDevice->defaultBounds()->wrapAroundMode());
0074     }
0075 
0076     QImage createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) {
0077         QImage thumbnail;
0078 
0079         if (h == 0 || w == 0) {
0080             return thumbnail;
0081         }
0082 
0083         if (m_thumbnailsValid) {
0084             thumbnail = findThumbnail(w, h, oversample);
0085         }
0086         else {
0087             m_thumbnails.clear();
0088             m_thumbnailsValid = true;
0089         }
0090 
0091         if (thumbnail.isNull()) {
0092             thumbnail = m_paintDevice->createThumbnail(w, h, QRect(), oversample, renderingIntent, conversionFlags);
0093             cacheThumbnail(w, h, oversample, thumbnail);
0094         }
0095 
0096         return thumbnail;
0097     }
0098 
0099     int sequenceNumber() const {
0100         return m_sequenceNumber;
0101     }
0102 
0103 private:
0104     inline QImage findThumbnail(qint32 w, qint32 h, qreal oversample) {
0105         QImage resultImage;
0106         if (m_thumbnails.contains(w) && m_thumbnails[w].contains(h) && m_thumbnails[w][h].contains(oversample)) {
0107             resultImage = m_thumbnails[w][h][oversample];
0108         }
0109         return resultImage;
0110     }
0111 
0112     inline void cacheThumbnail(qint32 w, qint32 h, qreal oversample, QImage image) {
0113         m_thumbnails[w][h][oversample] = image;
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     bool m_thumbnailsValid {false};
0154     QMap<int, QMap<int, QMap<qreal,QImage> > > m_thumbnails;
0155     QAtomicInt m_sequenceNumber;
0156 };
0157 
0158 #endif /* __KIS_PAINT_DEVICE_CACHE_H */