File indexing completed on 2024-06-09 04:24:38

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 #ifndef KIS_TEXTURE_TILE_UPDATE_INFO_H_
0007 #define KIS_TEXTURE_TILE_UPDATE_INFO_H_
0008 
0009 #include <QMessageBox>
0010 #include <QThreadStorage>
0011 #include <QScopedArrayPointer>
0012 
0013 #include "kis_config.h"
0014 #include "kis_image.h"
0015 #include "kis_paint_device.h"
0016 #include "kis_texture_tile_info_pool.h"
0017 #include <KoChannelInfo.h>
0018 #include <KoColorConversionTransformation.h>
0019 #include <KoColorModelStandardIds.h>
0020 #include <KoColorSpace.h>
0021 #include <kis_lod_transform.h>
0022 
0023 class KisTextureTileUpdateInfo;
0024 typedef QSharedPointer<KisTextureTileUpdateInfo> KisTextureTileUpdateInfoSP;
0025 typedef QVector<KisTextureTileUpdateInfoSP> KisTextureTileUpdateInfoSPList;
0026 
0027 /**
0028  * A buffer object for temporary data needed during the update process.
0029  *
0030  * - the buffer is allocated from the common pool to avoid memory
0031  *   fragmentation
0032  *
0033  * - the buffer's lifetime defines the lifetime of the allocated chunk
0034  *   of memory, so you don't have to thing about freeing the memory
0035  */
0036 
0037 class DataBuffer
0038 {
0039 public:
0040     DataBuffer(KisTextureTileInfoPoolSP pool)
0041         : m_data(0),
0042           m_pixelSize(0),
0043           m_pool(pool)
0044     {
0045     }
0046 
0047     DataBuffer(int pixelSize, KisTextureTileInfoPoolSP pool)
0048         : m_data(0),
0049           m_pixelSize(0),
0050           m_pool(pool)
0051     {
0052         allocate(pixelSize);
0053     }
0054 
0055     DataBuffer(DataBuffer &&rhs)
0056         : m_data(rhs.m_data),
0057           m_pixelSize(rhs.m_pixelSize),
0058           m_pool(rhs.m_pool)
0059     {
0060         rhs.m_data = 0;
0061     }
0062 
0063     DataBuffer& operator=(DataBuffer &&rhs) {
0064         swap(rhs);
0065         return *this;
0066     }
0067 
0068     ~DataBuffer() {
0069         if (m_data) {
0070             m_pool->free(m_data, m_pixelSize);
0071         }
0072     }
0073 
0074     void allocate(int pixelSize) {
0075         Q_ASSERT(!m_data);
0076 
0077         m_pixelSize = pixelSize;
0078         m_data = m_pool->malloc(m_pixelSize);
0079     }
0080 
0081     inline quint8* data() const {
0082         return m_data;
0083     }
0084 
0085     void swap(DataBuffer &other) {
0086         std::swap(other.m_pixelSize, m_pixelSize);
0087         std::swap(other.m_data, m_data);
0088         std::swap(other.m_pool, m_pool);
0089     }
0090 
0091     int size() const {
0092         return m_data ? m_pool->chunkSize(m_pixelSize) : 0;
0093     }
0094 
0095     KisTextureTileInfoPoolSP pool() const {
0096         return m_pool;
0097     }
0098 
0099     int pixelSize() const {
0100         return m_pixelSize;
0101     }
0102 
0103 private:
0104     Q_DISABLE_COPY(DataBuffer)
0105 
0106     quint8 *m_data {nullptr};
0107     int m_pixelSize;
0108     KisTextureTileInfoPoolSP m_pool;
0109 };
0110 
0111 class KisTextureTileUpdateInfo
0112 {
0113 public:
0114     KisTextureTileUpdateInfo(KisTextureTileInfoPoolSP pool)
0115         : m_patchPixels(pool),
0116           m_pool(pool)
0117     {
0118     }
0119 
0120     KisTextureTileUpdateInfo(qint32 col, qint32 row,
0121                              const QRect &tileRect, const QRect &updateRect, const QRect &currentImageRect,
0122                              int levelOfDetail,
0123                              KisTextureTileInfoPoolSP pool)
0124         : m_patchPixels(pool),
0125           m_pool(pool)
0126     {
0127         m_tileCol = col;
0128         m_tileRow = row;
0129         m_tileRect = tileRect;
0130         m_originalTileRect = m_tileRect;
0131         m_patchRect = m_tileRect & updateRect;
0132         m_originalPatchRect = m_patchRect;
0133         m_currentImageRect = currentImageRect;
0134 
0135         m_patchLevelOfDetail = levelOfDetail;
0136 
0137         if (m_patchLevelOfDetail) {
0138             // TODO: check if isBottommost() works correctly when m_originalPatchRect gets aligned
0139             //       and m_currentImageRect has non-aligned size
0140             m_originalPatchRect = KisLodTransform::alignedRect(m_originalPatchRect, m_patchLevelOfDetail);
0141             m_patchRect = KisLodTransform::scaledRect(m_originalPatchRect, m_patchLevelOfDetail);
0142             m_tileRect = KisLodTransform::scaledRect(m_originalTileRect, m_patchLevelOfDetail);
0143         }
0144     }
0145 
0146     ~KisTextureTileUpdateInfo() {
0147     }
0148 
0149     void retrieveData(KisPaintDeviceSP projectionDevice, const QBitArray &channelFlags, bool onlyOneChannelSelected, int selectedChannelIndex)
0150     {
0151         m_patchColorSpace = projectionDevice->colorSpace();
0152         m_patchPixels.allocate(m_patchColorSpace->pixelSize());
0153 
0154         projectionDevice->readBytes(m_patchPixels.data(),
0155                                        m_patchRect.x(), m_patchRect.y(),
0156                                        m_patchRect.width(), m_patchRect.height());
0157 
0158         // XXX: if the paint colorspace is rgb, we should do the channel swizzling in
0159         //      the display shader
0160         if (!channelFlags.isEmpty() && selectedChannelIndex >= 0 && selectedChannelIndex < m_patchColorSpace->channelCount()) {
0161             DataBuffer conversionCache(m_patchColorSpace->pixelSize(), m_pool);
0162 
0163             quint32 numPixels = m_patchRect.width() * m_patchRect.height();
0164 
0165             KisConfig cfg(true);
0166 
0167             if (onlyOneChannelSelected && !cfg.showSingleChannelAsColor()) {
0168                 m_patchColorSpace->convertChannelToVisualRepresentation(m_patchPixels.data(), conversionCache.data(), numPixels, selectedChannelIndex);
0169             } else {
0170                 m_patchColorSpace->convertChannelToVisualRepresentation(m_patchPixels.data(), conversionCache.data(), numPixels, channelFlags);
0171             }
0172 
0173             conversionCache.swap(m_patchPixels);
0174         }
0175 
0176     }
0177 
0178     void convertTo(const KoColorSpace* dstCS,
0179                    KoColorConversionTransformation::Intent renderingIntent,
0180                    KoColorConversionTransformation::ConversionFlags conversionFlags)
0181     {
0182         // we use two-stage check of the color space equivalence:
0183         // first check pointers, and if not, check the spaces themselves
0184         if ((dstCS == m_patchColorSpace || *dstCS == *m_patchColorSpace) &&
0185             conversionFlags == KoColorConversionTransformation::Empty) {
0186 
0187             return;
0188         }
0189 
0190         if (m_patchRect.isValid()) {
0191             const qint32 numPixels = m_patchRect.width() * m_patchRect.height();
0192             DataBuffer conversionCache(dstCS->pixelSize(), m_pool);
0193 
0194             m_patchColorSpace->convertPixelsTo(m_patchPixels.data(), conversionCache.data(), dstCS, numPixels, renderingIntent, conversionFlags);
0195 
0196             m_patchColorSpace = dstCS;
0197             conversionCache.swap(m_patchPixels);
0198         }
0199     }
0200 
0201     void proofTo(const KoColorSpace* dstCS,
0202                    KoColorConversionTransformation::ConversionFlags conversionFlags,
0203                    KoColorConversionTransformation *proofingTransform)
0204     {
0205         if (dstCS == m_patchColorSpace && conversionFlags == KoColorConversionTransformation::Empty) return;
0206 
0207         if (m_patchRect.isValid()) {
0208             const qint32 numPixels = m_patchRect.width() * m_patchRect.height();
0209             DataBuffer conversionCache(dstCS->pixelSize(), m_pool);
0210 
0211             m_patchColorSpace->proofPixelsTo(m_patchPixels.data(), conversionCache.data(), numPixels, proofingTransform);
0212 
0213             m_patchColorSpace = dstCS;
0214             conversionCache.swap(m_patchPixels);
0215         }
0216     }
0217 
0218     static KoColorConversionTransformation *generateProofingTransform(const KoColorSpace* srcCS,
0219                                                                       const KoColorSpace* dstCS, const KoColorSpace* proofingSpace,
0220                                                                       KoColorConversionTransformation::Intent renderingIntent,
0221                                                                       KoColorConversionTransformation::Intent proofingIntent,
0222                                                                       KoColorConversionTransformation::ConversionFlags conversionFlags,
0223                                                                       KoColor gamutWarning,
0224                                                                       double adaptationState)
0225     {
0226         return srcCS->createProofingTransform(dstCS, proofingSpace, renderingIntent, proofingIntent, conversionFlags, gamutWarning.data(), adaptationState);
0227     }
0228 
0229     inline quint8* data() const {
0230         return m_patchPixels.data();
0231     }
0232 
0233     inline int patchLevelOfDetail() const {
0234         return m_patchLevelOfDetail;
0235     }
0236 
0237     inline QPoint realPatchOffset() const {
0238         return QPoint(m_patchRect.x() - m_tileRect.x(),
0239                       m_patchRect.y() - m_tileRect.y());
0240     }
0241 
0242     inline QSize realPatchSize() const {
0243         return m_patchRect.size();
0244     }
0245 
0246     inline QRect realPatchRect() const {
0247         return m_patchRect;
0248     }
0249 
0250     inline QSize realTileSize() const {
0251         return m_tileRect.size();
0252     }
0253 
0254     inline bool isTopmost() const {
0255         return m_originalPatchRect.top() == m_currentImageRect.top();
0256     }
0257 
0258     inline bool isLeftmost() const {
0259         return m_originalPatchRect.left() == m_currentImageRect.left();
0260     }
0261 
0262     inline bool isRightmost() const {
0263         return m_originalPatchRect.right() == m_currentImageRect.right();
0264     }
0265 
0266     inline bool isBottommost() const {
0267         return m_originalPatchRect.bottom() == m_currentImageRect.bottom();
0268     }
0269 
0270     inline bool isEntireTileUpdated() const {
0271         return m_patchRect == m_tileRect;
0272     }
0273 
0274     inline qint32 tileCol() const {
0275         return m_tileCol;
0276     }
0277 
0278     inline qint32 tileRow() const {
0279         return m_tileRow;
0280     }
0281 
0282     inline int pixelSize() const {
0283         return m_patchColorSpace->pixelSize();
0284     }
0285 
0286     inline const KoColorSpace* patchColorSpace() const {
0287         return m_patchColorSpace;
0288     }
0289 
0290     inline quint32 patchPixelsLength() const {
0291         return m_patchPixels.size();
0292     }
0293 
0294     inline bool valid() const {
0295         return m_patchRect.isValid();
0296     }
0297 
0298     inline DataBuffer&& takePixelData() {
0299         return std::move(m_patchPixels);
0300     }
0301 
0302     inline void putPixelData(DataBuffer &&buffer, const KoColorSpace *colorSpace) {
0303         m_patchPixels = std::move(buffer);
0304         m_patchColorSpace = colorSpace;
0305     }
0306 
0307 private:
0308     Q_DISABLE_COPY(KisTextureTileUpdateInfo)
0309 
0310 private:
0311     qint32 m_tileCol {0};
0312     qint32 m_tileRow {0};
0313     QRect m_currentImageRect;
0314     QRect m_tileRect;
0315     QRect m_patchRect;
0316     const KoColorSpace* m_patchColorSpace {nullptr};
0317 
0318     QRect m_realPatchRect;
0319     QRect m_realPatchOffset;
0320     QRect m_realTileSize;
0321     int m_patchLevelOfDetail {0};
0322 
0323     QRect m_originalPatchRect;
0324     QRect m_originalTileRect;
0325 
0326     DataBuffer m_patchPixels;
0327     KisTextureTileInfoPoolSP m_pool;
0328 };
0329 
0330 
0331 #endif /* KIS_TEXTURE_TILE_UPDATE_INFO_H_ */
0332