File indexing completed on 2024-12-22 04:12:47
0001 /* 0002 * SPDX-FileCopyrightText: 2010 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #define GL_GLEXT_PROTOTYPES 0008 #include "kis_texture_tile.h" 0009 #include "kis_texture_tile_update_info.h" 0010 #include "KisOpenGLBufferCircularStorage.h" 0011 0012 #include <kis_debug.h> 0013 #if !defined(QT_OPENGL_ES) 0014 #include <QOpenGLBuffer> 0015 #endif 0016 0017 #ifndef GL_BGRA 0018 #define GL_BGRA 0x814F 0019 #endif 0020 0021 #ifndef GL_RGBA16_EXT 0022 #define GL_RGBA16_EXT 0x805B 0023 #endif 0024 0025 0026 void KisTextureTile::setTextureParameters() 0027 { 0028 0029 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 0030 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 0031 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); 0032 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, m_numMipmapLevels); 0033 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); 0034 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, m_numMipmapLevels); 0035 0036 if ((m_texturesInfo->internalFormat == GL_RGBA8 && m_texturesInfo->format == GL_RGBA) 0037 #ifndef QT_OPENGL_ES_2 0038 || (m_texturesInfo->internalFormat == GL_RGBA16 && m_texturesInfo->format == GL_RGBA) 0039 #endif 0040 || (m_texturesInfo->internalFormat == GL_RGBA16_EXT && m_texturesInfo->format == GL_RGBA) 0041 ) { 0042 // If image format is RGBA8, swap the red and blue channels for the proper color 0043 // This is for OpenGL ES support and only used if lacking GL_EXT_texture_format_BGRA8888 0044 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); 0045 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); 0046 } 0047 0048 f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 0049 } 0050 0051 void KisTextureTile::restoreTextureParameters() 0052 { 0053 // QPainter::drawText relies on this. 0054 // Ref: https://bugreports.qt.io/browse/QTBUG-65496 0055 f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 0056 } 0057 0058 inline QRectF relativeRect(const QRect &br /* baseRect */, 0059 const QRect &cr /* childRect */, 0060 const KisGLTexturesInfo *texturesInfo) 0061 { 0062 const qreal x = qreal(cr.x() - br.x()) / texturesInfo->width; 0063 const qreal y = qreal(cr.y() - br.y()) / texturesInfo->height; 0064 const qreal w = qreal(cr.width()) / texturesInfo->width; 0065 const qreal h = qreal(cr.height()) / texturesInfo->height; 0066 0067 return QRectF(x, y, w, h); 0068 } 0069 0070 #include "kis_debug.h" 0071 0072 KisTextureTile::KisTextureTile(const QRect &imageRect, const KisGLTexturesInfo *texturesInfo, 0073 const QByteArray &fillData, KisOpenGL::FilterMode filter, 0074 KisOpenGLBufferCircularStorage *bufferStorage, int numMipmapLevels, QOpenGLFunctions *fcn) 0075 0076 : m_textureId(0) 0077 , m_tileRectInImagePixels(imageRect) 0078 , m_filter(filter) 0079 , m_texturesInfo(texturesInfo) 0080 , m_needsMipmapRegeneration(false) 0081 , m_preparedLodPlane(0) 0082 , m_numMipmapLevels(numMipmapLevels) 0083 , f(fcn) 0084 , m_bufferStorage(bufferStorage) 0085 { 0086 const GLvoid *fd = fillData.constData(); 0087 0088 m_textureRectInImagePixels = 0089 kisGrowRect(m_tileRectInImagePixels, texturesInfo->border); 0090 0091 m_tileRectInTexturePixels = relativeRect(m_textureRectInImagePixels, 0092 m_tileRectInImagePixels, 0093 m_texturesInfo); 0094 0095 f->glGenTextures(1, &m_textureId); 0096 f->glBindTexture(GL_TEXTURE_2D, m_textureId); 0097 0098 setTextureParameters(); 0099 0100 KisOpenGLBufferCircularStorage::BufferBinder binder( 0101 m_bufferStorage, &fd, fillData.size()); 0102 0103 f->glTexImage2D(GL_TEXTURE_2D, 0, 0104 m_texturesInfo->internalFormat, 0105 m_texturesInfo->width, 0106 m_texturesInfo->height, 0, 0107 m_texturesInfo->format, 0108 m_texturesInfo->type, fd); 0109 0110 restoreTextureParameters(); 0111 0112 setNeedsMipmapRegeneration(); 0113 } 0114 0115 KisTextureTile::~KisTextureTile() 0116 { 0117 f->glDeleteTextures(1, &m_textureId); 0118 } 0119 0120 int KisTextureTile::bindToActiveTexture(bool blockMipmapRegeneration) 0121 { 0122 f->glBindTexture(GL_TEXTURE_2D, m_textureId); 0123 0124 if (m_needsMipmapRegeneration && !blockMipmapRegeneration) { 0125 f->glGenerateMipmap(GL_TEXTURE_2D); 0126 setPreparedLodPlane(0); 0127 } 0128 0129 return m_preparedLodPlane; 0130 } 0131 0132 void KisTextureTile::setNeedsMipmapRegeneration() 0133 { 0134 if (m_filter == KisOpenGL::TrilinearFilterMode || 0135 m_filter == KisOpenGL::HighQualityFiltering) { 0136 0137 m_needsMipmapRegeneration = true; 0138 } 0139 } 0140 0141 void KisTextureTile::setPreparedLodPlane(int lod) 0142 { 0143 m_preparedLodPlane = lod; 0144 m_needsMipmapRegeneration = false; 0145 } 0146 0147 void KisTextureTile::update(const KisTextureTileUpdateInfo &updateInfo, bool blockMipmapRegeneration) 0148 { 0149 f->initializeOpenGLFunctions(); 0150 f->glBindTexture(GL_TEXTURE_2D, m_textureId); 0151 0152 setTextureParameters(); 0153 0154 const int patchLevelOfDetail = updateInfo.patchLevelOfDetail(); 0155 const QSize patchSize = updateInfo.realPatchSize(); 0156 const QPoint patchOffset = updateInfo.realPatchOffset(); 0157 0158 const GLvoid *fd = updateInfo.data(); 0159 0160 /** 0161 * In some special case, when the Lod0 stroke is cancelled the 0162 * following situation is possible: 0163 * 0164 * 1) The stroke is cancelled, Lod0 update is issued by the 0165 * image. LodN level of the openGL times is still dirty. 0166 * 0167 * 2) [here, ideally, the canvas should be re-rendered, so that 0168 * the mipmap would be regenerated in bindToActiveTexture() 0169 * call, by in some cases (if you cancel and paint to quickly), 0170 * that doesn't have time to happen] 0171 * 0172 * 3) The new LodN stroke issues a *partial* update of a LodN 0173 * plane of the tile. But the plane is still *dirty*! We update 0174 * a part of it, but we cannot regenerate the mipmap anymore, 0175 * because the Lod0 level is not known yet! 0176 * 0177 * To avoid this issue, we should regenerate the dirty mipmap 0178 * *before* doing anything with the low-resolution plane. 0179 */ 0180 if (!blockMipmapRegeneration && 0181 patchLevelOfDetail > 0 && 0182 m_needsMipmapRegeneration && 0183 !updateInfo.isEntireTileUpdated()) { 0184 0185 f->glGenerateMipmap(GL_TEXTURE_2D); 0186 m_needsMipmapRegeneration = false; 0187 } 0188 0189 0190 if (updateInfo.isEntireTileUpdated()) { 0191 KisOpenGLBufferCircularStorage::BufferBinder b( 0192 m_bufferStorage, &fd, updateInfo.patchPixelsLength()); 0193 0194 f->glTexImage2D(GL_TEXTURE_2D, patchLevelOfDetail, 0195 m_texturesInfo->internalFormat, 0196 patchSize.width(), 0197 patchSize.height(), 0, 0198 m_texturesInfo->format, 0199 m_texturesInfo->type, 0200 fd); 0201 } 0202 else { 0203 const int size = patchSize.width() * patchSize.height() * updateInfo.pixelSize(); 0204 KisOpenGLBufferCircularStorage::BufferBinder b( 0205 m_bufferStorage, &fd, size); 0206 0207 f->glTexSubImage2D(GL_TEXTURE_2D, patchLevelOfDetail, 0208 patchOffset.x(), patchOffset.y(), 0209 patchSize.width(), patchSize.height(), 0210 m_texturesInfo->format, 0211 m_texturesInfo->type, 0212 fd); 0213 0214 } 0215 0216 /** 0217 * On the boundaries of KisImage, there is a border-effect as well. 0218 * So we just repeat the bounding pixels of the image to make 0219 * bilinear interpolator happy. 0220 */ 0221 0222 /** 0223 * WARN: The width of the stripes will be equal to the broader 0224 * width of the tiles. 0225 */ 0226 0227 const int pixelSize = updateInfo.pixelSize(); 0228 const QSize tileSize = updateInfo.realTileSize(); 0229 0230 if(updateInfo.isTopmost()) { 0231 int start = 0; 0232 int end = patchOffset.y() - 1; 0233 0234 const GLvoid *fd = updateInfo.data(); 0235 const int size = patchSize.width() * pixelSize; 0236 KisOpenGLBufferCircularStorage::BufferBinder g( 0237 m_bufferStorage, &fd, size); 0238 0239 for (int i = start; i <= end; i++) { 0240 f->glTexSubImage2D(GL_TEXTURE_2D, patchLevelOfDetail, 0241 patchOffset.x(), i, 0242 patchSize.width(), 1, 0243 m_texturesInfo->format, 0244 m_texturesInfo->type, 0245 fd); 0246 } 0247 } 0248 0249 if (updateInfo.isBottommost()) { 0250 int shift = patchSize.width() * (patchSize.height() - 1) * 0251 pixelSize; 0252 0253 int start = patchOffset.y() + patchSize.height(); 0254 int end = tileSize.height() - 1; 0255 0256 const GLvoid *fd = updateInfo.data() + shift; 0257 const int size = patchSize.width() * pixelSize; 0258 KisOpenGLBufferCircularStorage::BufferBinder g( 0259 m_bufferStorage, &fd, size); 0260 0261 for (int i = start; i < end; i++) { 0262 f->glTexSubImage2D(GL_TEXTURE_2D, patchLevelOfDetail, 0263 patchOffset.x(), i, 0264 patchSize.width(), 1, 0265 m_texturesInfo->format, 0266 m_texturesInfo->type, 0267 fd); 0268 } 0269 } 0270 0271 if (updateInfo.isLeftmost()) { 0272 0273 QByteArray columnBuffer(patchSize.height() * pixelSize, 0); 0274 0275 quint8 *srcPtr = updateInfo.data(); 0276 quint8 *dstPtr = (quint8*) columnBuffer.data(); 0277 for(int i = 0; i < patchSize.height(); i++) { 0278 memcpy(dstPtr, srcPtr, pixelSize); 0279 0280 srcPtr += patchSize.width() * pixelSize; 0281 dstPtr += pixelSize; 0282 } 0283 0284 int start = 0; 0285 int end = patchOffset.x() - 1; 0286 0287 const GLvoid *fd = columnBuffer.constData(); 0288 const int size = columnBuffer.size(); 0289 KisOpenGLBufferCircularStorage::BufferBinder g( 0290 m_bufferStorage, &fd, size); 0291 0292 for (int i = start; i <= end; i++) { 0293 f->glTexSubImage2D(GL_TEXTURE_2D, patchLevelOfDetail, 0294 i, patchOffset.y(), 0295 1, patchSize.height(), 0296 m_texturesInfo->format, 0297 m_texturesInfo->type, 0298 fd); 0299 } 0300 } 0301 0302 if (updateInfo.isRightmost()) { 0303 0304 QByteArray columnBuffer(patchSize.height() * pixelSize, 0); 0305 0306 quint8 *srcPtr = updateInfo.data() + (patchSize.width() - 1) * pixelSize; 0307 quint8 *dstPtr = (quint8*) columnBuffer.data(); 0308 for(int i = 0; i < patchSize.height(); i++) { 0309 memcpy(dstPtr, srcPtr, pixelSize); 0310 0311 srcPtr += patchSize.width() * pixelSize; 0312 dstPtr += pixelSize; 0313 } 0314 0315 int start = patchOffset.x() + patchSize.width(); 0316 int end = tileSize.width() - 1; 0317 0318 const GLvoid *fd = columnBuffer.constData(); 0319 const int size = columnBuffer.size(); 0320 KisOpenGLBufferCircularStorage::BufferBinder g( 0321 m_bufferStorage, &fd, size); 0322 0323 for (int i = start; i <= end; i++) { 0324 f->glTexSubImage2D(GL_TEXTURE_2D, patchLevelOfDetail, 0325 i, patchOffset.y(), 0326 1, patchSize.height(), 0327 m_texturesInfo->format, 0328 m_texturesInfo->type, 0329 fd); 0330 } 0331 } 0332 0333 //// Uncomment this warning if you see any weird flickering when 0334 //// Instant Preview updates 0335 // if (!updateInfo.isEntireTileUpdated() && 0336 // !(!patchLevelOfDetail || !m_preparedLodPlane || patchLevelOfDetail == m_preparedLodPlane)) { 0337 // qDebug() << "WARNING: LodN switch is requested for the partial tile update!. Flickering is possible..." << ppVar(patchSize); 0338 // qDebug() << " " << ppVar(m_preparedLodPlane); 0339 // qDebug() << " " << ppVar(patchLevelOfDetail); 0340 // } 0341 0342 restoreTextureParameters(); 0343 0344 if (!patchLevelOfDetail) { 0345 setNeedsMipmapRegeneration(); 0346 } else { 0347 setPreparedLodPlane(patchLevelOfDetail); 0348 } 0349 } 0350 0351 QRectF KisTextureTile::imageRectInTexturePixels(const QRect &imageRect) const 0352 { 0353 return relativeRect(m_textureRectInImagePixels, 0354 imageRect, 0355 m_texturesInfo); 0356 0357 }