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 ¤tImageRect, 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