File indexing completed on 2024-12-22 04:12:46
0001 /* 0002 * SPDX-FileCopyrightText: 2005-2007 Adrian Page <adrian@pagenet.plus.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "opengl/kis_opengl_image_textures.h" 0008 0009 #ifdef QT_OPENGL_ES_2 0010 #include <qopengl.h> 0011 #endif 0012 0013 #ifndef QT_OPENGL_ES_2 0014 #include <QOpenGLFunctions> 0015 #endif 0016 #include <QOpenGLContext> 0017 0018 #include <QMessageBox> 0019 #include <QApplication> 0020 #include <QDesktopWidget> 0021 0022 #include <KoColorSpaceRegistry.h> 0023 #include <KoColorProfile.h> 0024 #include <KoColorModelStandardIds.h> 0025 0026 #include "kis_image.h" 0027 #include "kis_config.h" 0028 #include "KisPart.h" 0029 #include "KisOpenGLModeProber.h" 0030 #include "kis_fixed_paint_device.h" 0031 #include "KisOpenGLSync.h" 0032 #include <QVector3D> 0033 #include "kis_painting_tweaks.h" 0034 #include "KisOpenGLBufferCreationGuard.h" 0035 0036 #ifdef HAVE_OPENEXR 0037 #include <half.h> 0038 #endif 0039 0040 #ifndef GL_CLAMP_TO_EDGE 0041 #define GL_CLAMP_TO_EDGE 0x812F 0042 #endif 0043 0044 #ifndef GL_BGRA 0045 #define GL_BGRA 0x80E1 0046 #endif 0047 0048 // GL_EXT_texture_format_BGRA8888 0049 #ifndef GL_BGRA_EXT 0050 #define GL_BGRA_EXT 0x80E1 0051 #endif 0052 #ifndef GL_BGRA8_EXT 0053 #define GL_BGRA8_EXT 0x93A1 0054 #endif 0055 0056 #ifndef GL_RGBA16_EXT 0057 #define GL_RGBA16_EXT 0x805B 0058 #endif 0059 0060 //#define DEBUG_BUFFER_REALLOCATION 0061 0062 0063 KisOpenGLImageTextures::ImageTexturesMap KisOpenGLImageTextures::imageTexturesMap; 0064 0065 KisOpenGLImageTextures::KisOpenGLImageTextures() 0066 : m_image(0) 0067 , m_monitorProfile(0) 0068 , m_internalColorManagementActive(true) 0069 , m_bufferStorage(QOpenGLBuffer::PixelUnpackBuffer) 0070 , m_glFuncs(0) 0071 , m_useOcio(false) 0072 , m_initialized(false) 0073 { 0074 KisConfig cfg(true); 0075 m_renderingIntent = (KoColorConversionTransformation::Intent)cfg.monitorRenderIntent(); 0076 0077 m_conversionFlags = KoColorConversionTransformation::HighQuality; 0078 if (cfg.useBlackPointCompensation()) m_conversionFlags |= KoColorConversionTransformation::BlackpointCompensation; 0079 if (!cfg.allowLCMSOptimization()) m_conversionFlags |= KoColorConversionTransformation::NoOptimization; 0080 m_useOcio = cfg.useOcio(); 0081 } 0082 0083 KisOpenGLImageTextures::KisOpenGLImageTextures(KisImageWSP image, 0084 const KoColorProfile *monitorProfile, 0085 KoColorConversionTransformation::Intent renderingIntent, 0086 KoColorConversionTransformation::ConversionFlags conversionFlags) 0087 : m_image(image) 0088 , m_monitorProfile(monitorProfile) 0089 , m_renderingIntent(renderingIntent) 0090 , m_conversionFlags(conversionFlags) 0091 , m_internalColorManagementActive(true) 0092 , m_bufferStorage(QOpenGLBuffer::PixelUnpackBuffer) 0093 , m_glFuncs(0) 0094 , m_useOcio(false) 0095 , m_initialized(false) 0096 { 0097 Q_ASSERT(renderingIntent < 4); 0098 } 0099 0100 void KisOpenGLImageTextures::initGL(QOpenGLFunctions *f) 0101 { 0102 if (f) { 0103 m_glFuncs = f; 0104 } else { 0105 errUI << "Tried to create OpenGLImageTextures with uninitialized QOpenGLFunctions"; 0106 } 0107 0108 getTextureSize(&m_texturesInfo); 0109 0110 // we use local static object for creating pools shared among 0111 // different images 0112 static KisTextureTileInfoPoolRegistry s_poolRegistry; 0113 m_updateInfoBuilder.setTextureInfoPool(s_poolRegistry.getPool(m_texturesInfo.width, m_texturesInfo.height)); 0114 0115 m_checkerTexture = GLuint(); 0116 m_glFuncs->glGenTextures(1, &(*m_checkerTexture)); 0117 recreateImageTextureTiles(); 0118 0119 KisOpenGLUpdateInfoSP info = updateCache(m_image->bounds(), m_image); 0120 recalculateCache(info, false); 0121 } 0122 0123 KisOpenGLImageTextures::~KisOpenGLImageTextures() 0124 { 0125 ImageTexturesMap::iterator it = imageTexturesMap.find(m_image); 0126 if (it != imageTexturesMap.end()) { 0127 KisOpenGLImageTextures *textures = it.value(); 0128 if (textures == this) { 0129 dbgUI << "Removing shared image context from map"; 0130 imageTexturesMap.erase(it); 0131 } 0132 } 0133 0134 destroyImageTextureTiles(); 0135 if (m_checkerTexture) { 0136 m_glFuncs->glDeleteTextures(1, &(*m_checkerTexture)); 0137 } 0138 } 0139 0140 KisImageSP KisOpenGLImageTextures::image() const 0141 { 0142 return m_image; 0143 } 0144 0145 bool KisOpenGLImageTextures::imageCanShareTextures() 0146 { 0147 KisConfig cfg(true); 0148 if (cfg.useOcio()) return false; 0149 if (KisPart::instance()->mainwindowCount() == 1) return true; 0150 if (QGuiApplication::screens().count() == 1) return true; 0151 for (int i = 1; i < QGuiApplication::screens().count(); i++) { 0152 if (cfg.displayProfile(i) != cfg.displayProfile(i - 1)) { 0153 return false; 0154 } 0155 } 0156 return true; 0157 } 0158 0159 void KisOpenGLImageTextures::initBufferStorage(bool useBuffer) 0160 { 0161 if (useBuffer) { 0162 const int numTextureBuffers = 16; 0163 0164 const KoColorSpace *tilesDestinationColorSpace = 0165 m_updateInfoBuilder.destinationColorSpace(); 0166 const int pixelSize = tilesDestinationColorSpace->pixelSize(); 0167 const int tileSize = m_texturesInfo.width * m_texturesInfo.height * pixelSize; 0168 0169 m_bufferStorage.allocate(numTextureBuffers, tileSize); 0170 } else { 0171 m_bufferStorage.reset(); 0172 } 0173 } 0174 0175 KisOpenGLImageTexturesSP KisOpenGLImageTextures::getImageTextures(KisImageWSP image, 0176 const KoColorProfile *monitorProfile, 0177 KoColorConversionTransformation::Intent renderingIntent, 0178 KoColorConversionTransformation::ConversionFlags conversionFlags) 0179 { 0180 // Disabled until we figure out why we're deleting the shared textures on closing the second view on a single image 0181 if (false && imageCanShareTextures()) { 0182 ImageTexturesMap::iterator it = imageTexturesMap.find(image); 0183 0184 if (it != imageTexturesMap.end()) { 0185 KisOpenGLImageTexturesSP textures = it.value(); 0186 textures->setMonitorProfile(monitorProfile, renderingIntent, conversionFlags); 0187 0188 return textures; 0189 } else { 0190 KisOpenGLImageTextures *imageTextures = new KisOpenGLImageTextures(image, monitorProfile, renderingIntent, conversionFlags); 0191 imageTexturesMap[image] = imageTextures; 0192 dbgUI << "Added shareable textures to map"; 0193 0194 return imageTextures; 0195 } 0196 } else { 0197 return new KisOpenGLImageTextures(image, monitorProfile, renderingIntent, conversionFlags); 0198 } 0199 } 0200 0201 void KisOpenGLImageTextures::recreateImageTextureTiles() 0202 { 0203 0204 destroyImageTextureTiles(); 0205 updateTextureFormat(); 0206 0207 const KoColorSpace *tilesDestinationColorSpace = 0208 m_updateInfoBuilder.destinationColorSpace(); 0209 0210 if (!tilesDestinationColorSpace) { 0211 qDebug() << "No destination colorspace!!!!"; 0212 return; 0213 } 0214 0215 0216 m_storedImageBounds = m_image->bounds(); 0217 const int lastCol = xToCol(m_image->width()); 0218 const int lastRow = yToRow(m_image->height()); 0219 0220 m_numCols = lastCol + 1; 0221 0222 // Default color is transparent black 0223 const int pixelSize = tilesDestinationColorSpace->pixelSize(); 0224 QByteArray emptyTileData((m_texturesInfo.width) * (m_texturesInfo.height) * pixelSize, 0); 0225 0226 KisConfig config(true); 0227 KisOpenGL::FilterMode mode = (KisOpenGL::FilterMode)config.openGLFilteringMode(); 0228 0229 initBufferStorage(KisOpenGL::shouldUseTextureBuffers(config.useOpenGLTextureBuffer())); 0230 0231 QOpenGLContext *ctx = QOpenGLContext::currentContext(); 0232 if (ctx) { 0233 QOpenGLFunctions *f = ctx->functions(); 0234 0235 m_initialized = true; 0236 dbgUI << "OpenGL: creating texture tiles of size" << m_texturesInfo.height << "x" << m_texturesInfo.width; 0237 0238 QVector<QRectF> tileImageRect; 0239 QVector<QRectF> tileTextureRect; 0240 0241 m_textureTiles.reserve((lastRow+1)*m_numCols); 0242 0243 tileImageRect.reserve(m_textureTiles.size()); 0244 tileTextureRect.reserve(m_textureTiles.size()); 0245 0246 for (int row = 0; row <= lastRow; row++) { 0247 for (int col = 0; col <= lastCol; col++) { 0248 QRect tileRect = m_updateInfoBuilder.calculateEffectiveTileRect(col, row, m_image->bounds()); 0249 0250 KisTextureTile *tile = new KisTextureTile(tileRect, 0251 &m_texturesInfo, 0252 emptyTileData, 0253 mode, 0254 m_bufferStorage.isValid() ? &m_bufferStorage : 0, 0255 config.numMipmapLevels(), 0256 f); 0257 m_textureTiles.append(tile); 0258 tileImageRect.append(tile->tileRectInImagePixels()); 0259 tileTextureRect.append(tile->tileRectInTexturePixels()); 0260 } 0261 } 0262 0263 0264 0265 { 0266 KisOpenGLBufferCreationGuard bufferGuard(&m_tileVertexBuffer, 0267 tileImageRect.size() * 6 * 3 * sizeof(float), 0268 QOpenGLBuffer::StaticDraw); 0269 0270 QVector3D* mappedPtr = reinterpret_cast<QVector3D*>(bufferGuard.data()); 0271 0272 Q_FOREACH (const QRectF &rc, tileImageRect) { 0273 KisPaintingTweaks::rectToVertices(mappedPtr, rc); 0274 mappedPtr += 6; 0275 } 0276 } 0277 0278 { 0279 KisOpenGLBufferCreationGuard bufferGuard(&m_tileTexCoordBuffer, 0280 tileImageRect.size() * 6 * 2 * sizeof(float), 0281 QOpenGLBuffer::StaticDraw); 0282 0283 QVector2D* mappedPtr = reinterpret_cast<QVector2D*>(bufferGuard.data()); 0284 0285 Q_FOREACH (const QRectF &rc, tileTextureRect) { 0286 KisPaintingTweaks::rectToTexCoords(mappedPtr, rc); 0287 mappedPtr += 6; 0288 } 0289 } 0290 } 0291 else { 0292 dbgUI << "Tried to init texture tiles without a current OpenGL Context."; 0293 } 0294 } 0295 0296 void KisOpenGLImageTextures::destroyImageTextureTiles() 0297 { 0298 if (m_textureTiles.isEmpty()) return; 0299 0300 Q_FOREACH (KisTextureTile *tile, m_textureTiles) { 0301 delete tile; 0302 } 0303 m_textureTiles.clear(); 0304 m_tileVertexBuffer.destroy(); 0305 m_tileTexCoordBuffer.destroy(); 0306 m_storedImageBounds = QRect(); 0307 } 0308 0309 KisOpenGLUpdateInfoSP KisOpenGLImageTextures::updateCache(const QRect& rect, KisImageSP srcImage) 0310 { 0311 return updateCacheImpl(rect, srcImage, true); 0312 } 0313 0314 KisOpenGLUpdateInfoSP KisOpenGLImageTextures::updateCacheNoConversion(const QRect& rect) 0315 { 0316 return updateCacheImpl(rect, m_image, false); 0317 } 0318 0319 // TODO: add sanity checks about the conformance of the passed srcImage! 0320 KisOpenGLUpdateInfoSP KisOpenGLImageTextures::updateCacheImpl(const QRect& rect, KisImageSP srcImage, bool convertColorSpace) 0321 { 0322 if (!m_initialized) return new KisOpenGLUpdateInfo(); 0323 return m_updateInfoBuilder.buildUpdateInfo(rect, srcImage, convertColorSpace); 0324 } 0325 0326 void KisOpenGLImageTextures::recalculateCache(KisUpdateInfoSP info, bool blockMipmapRegeneration) 0327 { 0328 if (!m_initialized) { 0329 dbgUI << "OpenGL: Tried to edit image texture cache before it was initialized."; 0330 return; 0331 } 0332 0333 KisOpenGLUpdateInfoSP glInfo = dynamic_cast<KisOpenGLUpdateInfo*>(info.data()); 0334 if(!glInfo) return; 0335 0336 QScopedPointer<KisOpenGLSync> sync; 0337 int numProcessedTiles = 0; 0338 0339 KisTextureTileUpdateInfoSP tileInfo; 0340 Q_FOREACH (tileInfo, glInfo->tileList) { 0341 KisTextureTile *tile = getTextureTileCR(tileInfo->tileCol(), tileInfo->tileRow()); 0342 KIS_ASSERT_RECOVER_RETURN(tile); 0343 0344 if (m_bufferStorage.isValid() && numProcessedTiles > m_bufferStorage.size() && 0345 sync && !sync->isSignaled()) { 0346 0347 #ifdef DEBUG_BUFFER_REALLOCATION 0348 qDebug() << "Still unsignalled after processed" << numProcessedTiles << "tiles"; 0349 #endif 0350 0351 m_bufferStorage.allocateMoreBuffers(); 0352 0353 #ifdef DEBUG_BUFFER_REALLOCATION 0354 qDebug() << " increased number of buffers to" << m_bufferStorage.size(); 0355 #endif 0356 } 0357 0358 0359 tile->update(*tileInfo, blockMipmapRegeneration); 0360 0361 if (m_bufferStorage.isValid()) { 0362 if (!sync) { 0363 sync.reset(new KisOpenGLSync()); 0364 numProcessedTiles = 0; 0365 } else if (sync && sync->isSignaled()) { 0366 sync.reset(); 0367 } 0368 numProcessedTiles++; 0369 } 0370 } 0371 } 0372 0373 void KisOpenGLImageTextures::generateCheckerTexture(const QImage &checkImage) 0374 { 0375 if (!m_initialized) { 0376 return; 0377 } 0378 0379 QOpenGLContext *ctx = QOpenGLContext::currentContext(); 0380 if (ctx) { 0381 QOpenGLFunctions *f = ctx->functions(); 0382 dbgUI << "Attaching checker texture" << checkerTexture(); 0383 f->glBindTexture(GL_TEXTURE_2D, checkerTexture()); 0384 0385 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 0386 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 0387 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 0388 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 0389 0390 f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 0391 0392 QImage img = checkImage; 0393 if (checkImage.width() != BACKGROUND_TEXTURE_SIZE || checkImage.height() != BACKGROUND_TEXTURE_SIZE) { 0394 img = checkImage.scaled(BACKGROUND_TEXTURE_SIZE, BACKGROUND_TEXTURE_SIZE); 0395 } 0396 0397 // convert from sRGB to display format, potentially HDR 0398 const KoColorSpace *temporaryColorSpace = KoColorSpaceRegistry::instance()->rgb8(); 0399 const KoColorSpace *finalColorSpace = 0400 KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), 0401 m_updateInfoBuilder.destinationColorSpace()->colorDepthId().id(), 0402 m_monitorProfile); 0403 0404 KisFixedPaintDevice checkers(temporaryColorSpace); 0405 checkers.convertFromQImage(img, temporaryColorSpace->profile()->name()); 0406 checkers.convertTo(finalColorSpace); 0407 0408 KIS_ASSERT(checkers.bounds().width() == BACKGROUND_TEXTURE_SIZE); 0409 KIS_ASSERT(checkers.bounds().height() == BACKGROUND_TEXTURE_SIZE); 0410 0411 GLint format = m_texturesInfo.format; 0412 GLint internalFormat = m_texturesInfo.internalFormat; 0413 GLint type = m_texturesInfo.type; 0414 0415 f->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, BACKGROUND_TEXTURE_SIZE, BACKGROUND_TEXTURE_SIZE, 0416 0, format, type, checkers.data()); 0417 0418 // QPainter::drawText relies on this. 0419 // Ref: https://bugreports.qt.io/browse/QTBUG-65496 0420 f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 0421 } 0422 else { 0423 dbgUI << "OpenGL: Tried to generate checker texture before OpenGL was initialized."; 0424 } 0425 0426 } 0427 0428 GLuint KisOpenGLImageTextures::checkerTexture() 0429 { 0430 if (m_glFuncs) { 0431 if (!m_checkerTexture) { 0432 m_checkerTexture = GLuint(); 0433 m_glFuncs->glGenTextures(1, &(*m_checkerTexture)); 0434 } 0435 return *m_checkerTexture; 0436 } 0437 else { 0438 dbgUI << "Tried to access checker texture before OpenGL was initialized"; 0439 return 0; 0440 } 0441 } 0442 0443 void KisOpenGLImageTextures::updateConfig(bool useBuffer, int NumMipmapLevels) 0444 { 0445 if(m_textureTiles.isEmpty()) return; 0446 0447 const bool effectiveUseBuffer = KisOpenGL::shouldUseTextureBuffers(useBuffer); 0448 initBufferStorage(effectiveUseBuffer); 0449 0450 Q_FOREACH (KisTextureTile *tile, m_textureTiles) { 0451 tile->setBufferStorage(effectiveUseBuffer ? &m_bufferStorage : 0); 0452 tile->setNumMipmapLevels(NumMipmapLevels); 0453 } 0454 } 0455 0456 void KisOpenGLImageTextures::testingForceInitialized() 0457 { 0458 m_initialized = true; 0459 m_updateInfoBuilder.setTextureInfoPool(toQShared(new KisTextureTileInfoPool(256, 256))); 0460 0461 ConversionOptions options; 0462 options.m_destinationColorSpace = KoColorSpaceRegistry::instance()->rgb8(); 0463 options.m_conversionFlags = KoColorConversionTransformation::internalConversionFlags(); 0464 options.m_renderingIntent = KoColorConversionTransformation::internalRenderingIntent(); 0465 options.m_needsConversion = false; 0466 m_updateInfoBuilder.setConversionOptions(options); 0467 0468 m_updateInfoBuilder.setTextureBorder(4); 0469 m_updateInfoBuilder.setEffectiveTextureSize(QSize(248, 248)); 0470 } 0471 0472 void KisOpenGLImageTextures::slotImageSizeChanged(qint32 /*w*/, qint32 /*h*/) 0473 { 0474 recreateImageTextureTiles(); 0475 } 0476 0477 KisOpenGLUpdateInfoBuilder &KisOpenGLImageTextures::updateInfoBuilder() 0478 { 0479 return m_updateInfoBuilder; 0480 } 0481 0482 const KoColorProfile *KisOpenGLImageTextures::monitorProfile() 0483 { 0484 return m_monitorProfile; 0485 } 0486 0487 void KisOpenGLImageTextures::setMonitorProfile(const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) 0488 { 0489 //dbgUI << "Setting monitor profile to" << monitorProfile->name() << renderingIntent << conversionFlags; 0490 m_monitorProfile = monitorProfile; 0491 m_renderingIntent = renderingIntent; 0492 m_conversionFlags = conversionFlags; 0493 0494 recreateImageTextureTiles(); 0495 } 0496 0497 bool KisOpenGLImageTextures::setImageColorSpace(const KoColorSpace *cs) 0498 { 0499 Q_UNUSED(cs); 0500 // TODO: implement lazy update: do not re-upload textures when not needed 0501 0502 recreateImageTextureTiles(); 0503 return true; 0504 } 0505 0506 void KisOpenGLImageTextures::setChannelFlags(const QBitArray &channelFlags) 0507 { 0508 QBitArray targetChannelFlags = channelFlags; 0509 int selectedChannels = 0; 0510 const KoColorSpace *projectionCs = m_image->projection()->colorSpace(); 0511 const QList<KoChannelInfo*> channelInfo = projectionCs->channels(); 0512 0513 if (targetChannelFlags.size() != channelInfo.size()) { 0514 targetChannelFlags = QBitArray(); 0515 } 0516 0517 int selectedChannelIndex = -1; 0518 0519 for (int i = 0; i < targetChannelFlags.size(); ++i) { 0520 if (targetChannelFlags.testBit(i) && channelInfo[i]->channelType() == KoChannelInfo::COLOR) { 0521 selectedChannels++; 0522 selectedChannelIndex = i; 0523 } 0524 } 0525 const bool allChannelsSelected = (selectedChannels == targetChannelFlags.size()); 0526 const bool onlyOneChannelSelected = (selectedChannels == 1); 0527 0528 // OCIO has its own channel swizzling 0529 if (allChannelsSelected || m_useOcio) { 0530 m_updateInfoBuilder.setChannelFlags(QBitArray(), false, -1); 0531 } else { 0532 m_updateInfoBuilder.setChannelFlags(targetChannelFlags, onlyOneChannelSelected, selectedChannelIndex); 0533 } 0534 } 0535 0536 void KisOpenGLImageTextures::setProofingConfig(KisProofingConfigurationSP proofingConfig) 0537 { 0538 m_updateInfoBuilder.setProofingConfig(proofingConfig); 0539 } 0540 0541 void KisOpenGLImageTextures::getTextureSize(KisGLTexturesInfo *texturesInfo) 0542 { 0543 KisConfig cfg(true); 0544 0545 const GLint preferredTextureSize = cfg.openGLTextureSize(); 0546 0547 GLint maxTextureSize; 0548 if (m_glFuncs) { 0549 m_glFuncs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); 0550 } 0551 else { 0552 dbgUI << "OpenGL: Tried to read texture size before OpenGL was initialized."; 0553 maxTextureSize = GL_MAX_TEXTURE_SIZE; 0554 } 0555 0556 texturesInfo->width = qMin(preferredTextureSize, maxTextureSize); 0557 texturesInfo->height = qMin(preferredTextureSize, maxTextureSize); 0558 0559 texturesInfo->border = cfg.textureOverlapBorder(); 0560 0561 texturesInfo->effectiveWidth = texturesInfo->width - 2 * texturesInfo->border; 0562 texturesInfo->effectiveHeight = texturesInfo->height - 2 * texturesInfo->border; 0563 0564 m_updateInfoBuilder.setTextureBorder(texturesInfo->border); 0565 m_updateInfoBuilder.setEffectiveTextureSize( 0566 QSize(texturesInfo->effectiveWidth, texturesInfo->effectiveHeight)); 0567 } 0568 0569 bool KisOpenGLImageTextures::internalColorManagementActive() const 0570 { 0571 return m_internalColorManagementActive; 0572 } 0573 0574 bool KisOpenGLImageTextures::setInternalColorManagementActive(bool value) 0575 { 0576 bool needsFinalRegeneration = m_internalColorManagementActive != value; 0577 0578 if (needsFinalRegeneration) { 0579 m_internalColorManagementActive = value; 0580 recreateImageTextureTiles(); 0581 0582 // at this point the value of m_internalColorManagementActive might 0583 // have been forcefully reverted to 'false' in case of some problems 0584 } 0585 0586 return needsFinalRegeneration; 0587 } 0588 0589 namespace { 0590 void initializeRGBA16FTextures(QOpenGLContext *ctx, KisGLTexturesInfo &texturesInfo, KoID &destinationColorDepthId) 0591 { 0592 bool haveBuiltInOpenExr = false; 0593 #ifdef HAVE_OPENEXR 0594 haveBuiltInOpenExr = true; 0595 #endif 0596 0597 if (haveBuiltInOpenExr && (KisOpenGL::hasOpenGLES() || KisOpenGL::hasOpenGL3())) { 0598 #ifndef QT_OPENGL_ES_2 0599 texturesInfo.internalFormat = GL_RGBA16F; 0600 dbgUI << "Using half (GLES or GL3)"; 0601 texturesInfo.type = GL_HALF_FLOAT; 0602 destinationColorDepthId = Float16BitsColorDepthID; 0603 dbgUI << "Pixel type half (GLES or GL3)"; 0604 texturesInfo.format = GL_RGBA; 0605 } else if (haveBuiltInOpenExr && ctx->hasExtension("GL_ARB_half_float_pixel")) { 0606 texturesInfo.internalFormat = GL_RGBA16F_ARB; 0607 dbgUI << "Using ARB half"; 0608 texturesInfo.type = GL_HALF_FLOAT_ARB; 0609 destinationColorDepthId = Float16BitsColorDepthID; 0610 texturesInfo.format = GL_RGBA; 0611 dbgUI << "Pixel type half"; 0612 } else if (haveBuiltInOpenExr && ctx->hasExtension("GL_ATI_texture_float")) { 0613 texturesInfo.internalFormat = GL_RGBA_FLOAT16_ATI; 0614 dbgUI << "Using ATI half"; 0615 texturesInfo.type = GL_HALF_FLOAT; 0616 destinationColorDepthId = Float16BitsColorDepthID; 0617 dbgUI << "Using half (GLES or GL3)"; 0618 texturesInfo.format = GL_RGBA; 0619 } else { 0620 texturesInfo.internalFormat = GL_RGBA32F; 0621 texturesInfo.type = GL_FLOAT; 0622 destinationColorDepthId = Float32BitsColorDepthID; 0623 dbgUI << "Pixel type float"; 0624 texturesInfo.format = GL_RGBA; 0625 #else 0626 if (ctx->format().majorVersion() >= 3) { 0627 texturesInfo.internalFormat = GL_RGBA16F; 0628 dbgUI << "Using half (GLES 3 non-Angle)"; 0629 texturesInfo.type = GL_HALF_FLOAT; 0630 destinationColorDepthId = Float16BitsColorDepthID; 0631 dbgUI << "Pixel type half (GLES 3 non-Angle)"; 0632 texturesInfo.format = GL_RGBA; 0633 } else if (ctx->hasExtension("GL_OES_texture_half_float") && ctx->hasExtension("GL_EXT_color_buffer_half_float") 0634 && ctx->hasExtension("GL_OES_texture_half_float_linear")) { 0635 texturesInfo.internalFormat = GL_RGBA16F_EXT; 0636 dbgUI << "Using half (GLES v2)"; 0637 texturesInfo.type = GL_HALF_FLOAT_OES; 0638 destinationColorDepthId = Float16BitsColorDepthID; 0639 dbgUI << "Pixel type half (GLES v2)"; 0640 texturesInfo.format = GL_RGBA; 0641 } else if (ctx->hasExtension("GL_OES_texture_float") && (ctx->hasExtension("GL_EXT_texture_storage") || ctx->hasExtension("EXT_color_buffer_float")) 0642 && ctx->hasExtension("GL_OES_texture_float_linear")) { 0643 texturesInfo.internalFormat = GL_RGBA32F_EXT; 0644 dbgUI << "Using float (GLES v2)"; 0645 texturesInfo.type = GL_FLOAT; 0646 destinationColorDepthId = Float32BitsColorDepthID; 0647 dbgUI << "Pixel type float (GLES v2)"; 0648 texturesInfo.format = GL_RGBA; 0649 } 0650 } else if (ctx->format().majorVersion() >= 3) { 0651 texturesInfo.internalFormat = GL_RGBA32F; 0652 dbgUI << "Using float (GLES 3 non-Angle)"; 0653 texturesInfo.type = GL_FLOAT; 0654 destinationColorDepthId = Float32BitsColorDepthID; 0655 dbgUI << "Pixel type float (GLES 3 non-Angle)"; 0656 texturesInfo.format = GL_RGBA; 0657 } else if (ctx->hasExtension("GL_OES_texture_float") && (ctx->hasExtension("GL_EXT_texture_storage") || ctx->hasExtension("EXT_color_buffer_float")) 0658 && ctx->hasExtension("GL_OES_texture_float_linear")) { 0659 texturesInfo.internalFormat = GL_RGBA32F_EXT; 0660 dbgUI << "Using float (GLES v2)"; 0661 texturesInfo.type = GL_FLOAT; 0662 destinationColorDepthId = Float32BitsColorDepthID; 0663 dbgUI << "Pixel type float (GLES v2)"; 0664 texturesInfo.format = GL_RGBA; 0665 #endif 0666 } 0667 } 0668 } 0669 0670 void KisOpenGLImageTextures::updateTextureFormat() 0671 { 0672 QOpenGLContext *ctx = QOpenGLContext::currentContext(); 0673 if (!(m_image && ctx)) return; 0674 0675 if (!KisOpenGL::hasOpenGLES()) { 0676 #ifndef QT_OPENGL_ES_2 0677 m_texturesInfo.internalFormat = GL_RGBA8; 0678 m_texturesInfo.type = GL_UNSIGNED_BYTE; 0679 m_texturesInfo.format = GL_BGRA; 0680 #else 0681 KIS_ASSERT_X(false, "KisOpenGLImageTextures::updateTextureFormat", 0682 "Unexpected KisOpenGL::hasOpenGLES returned false"); 0683 #endif 0684 } else { 0685 #ifdef QT_OPENGL_ES_2 0686 m_texturesInfo.internalFormat = GL_RGBA8; 0687 m_texturesInfo.type = GL_UNSIGNED_BYTE; 0688 m_texturesInfo.format = GL_RGBA; 0689 #else 0690 m_texturesInfo.internalFormat = GL_BGRA8_EXT; 0691 m_texturesInfo.type = GL_UNSIGNED_BYTE; 0692 m_texturesInfo.format = GL_BGRA_EXT; 0693 if(!ctx->hasExtension(QByteArrayLiteral("GL_EXT_texture_format_BGRA8888"))) { 0694 // The red and blue channels are swapped, but it will be re-swapped 0695 // by texture swizzle mask set in KisTextureTile::setTextureParameters 0696 m_texturesInfo.internalFormat = GL_RGBA8; 0697 m_texturesInfo.type = GL_UNSIGNED_BYTE; 0698 m_texturesInfo.format = GL_RGBA; 0699 } 0700 #endif 0701 } 0702 0703 const bool useHDRMode = KisOpenGLModeProber::instance()->useHDRMode(); 0704 const KoID colorModelId = m_image->colorSpace()->colorModelId(); 0705 const KoID colorDepthId = useHDRMode ? Float16BitsColorDepthID : m_image->colorSpace()->colorDepthId(); 0706 0707 KoID destinationColorModelId = RGBAColorModelID; 0708 KoID destinationColorDepthId = Integer8BitsColorDepthID; 0709 0710 dbgUI << "Choosing texture format:"; 0711 0712 if (colorModelId == RGBAColorModelID) { 0713 if (colorDepthId == Float16BitsColorDepthID) { 0714 initializeRGBA16FTextures(ctx, m_texturesInfo, destinationColorDepthId); 0715 } 0716 else if (colorDepthId == Float32BitsColorDepthID) { 0717 if (KisOpenGL::hasOpenGLES() || KisOpenGL::hasOpenGL3()) { 0718 #ifndef QT_OPENGL_ES_2 0719 m_texturesInfo.internalFormat = GL_RGBA32F; 0720 dbgUI << "Using float (GLES or GL3)"; 0721 m_texturesInfo.type = GL_FLOAT; 0722 m_texturesInfo.format = GL_RGBA; 0723 destinationColorDepthId = Float32BitsColorDepthID; 0724 } else if (ctx->hasExtension("GL_ARB_texture_float")) { 0725 m_texturesInfo.internalFormat = GL_RGBA32F_ARB; 0726 dbgUI << "Using ARB float"; 0727 m_texturesInfo.type = GL_FLOAT; 0728 m_texturesInfo.format = GL_RGBA; 0729 destinationColorDepthId = Float32BitsColorDepthID; 0730 } else if (ctx->hasExtension("GL_ATI_texture_float")) { 0731 m_texturesInfo.internalFormat = GL_RGBA_FLOAT32_ATI; 0732 dbgUI << "Using ATI float"; 0733 m_texturesInfo.type = GL_FLOAT; 0734 m_texturesInfo.format = GL_RGBA; 0735 destinationColorDepthId = Float32BitsColorDepthID; 0736 #else 0737 if (ctx->format().majorVersion() >= 3) { 0738 m_texturesInfo.internalFormat = GL_RGBA32F; 0739 dbgUI << "Using float (GLES 3 non-Angle)"; 0740 m_texturesInfo.type = GL_FLOAT; 0741 m_texturesInfo.format = GL_RGBA; 0742 destinationColorDepthId = Float32BitsColorDepthID; 0743 /* GL_EXT_texture_storage is GLES 2*/ 0744 /* GL_EXT_color_buffer_float is GLES 3*/ 0745 } else if (ctx->hasExtension("GL_OES_texture_float") 0746 && (ctx->hasExtension("GL_EXT_texture_storage") || ctx->hasExtension("EXT_color_buffer_float")) 0747 && ctx->hasExtension("GL_OES_texture_float_linear")) { 0748 m_texturesInfo.internalFormat = GL_RGBA32F_EXT; 0749 dbgUI << "Using float (GLES v2)"; 0750 m_texturesInfo.type = GL_FLOAT; 0751 m_texturesInfo.format = GL_RGBA; 0752 destinationColorDepthId = Float32BitsColorDepthID; 0753 } 0754 #endif 0755 } 0756 } 0757 else if (colorDepthId == Integer16BitsColorDepthID) { 0758 if (!KisOpenGL::hasOpenGLES()) { 0759 #ifndef QT_OPENGL_ES_2 0760 m_texturesInfo.internalFormat = GL_RGBA16; 0761 m_texturesInfo.type = GL_UNSIGNED_SHORT; 0762 0763 // On arm M1, GL_BGRA format is not aligned properly at the driver 0764 // changing the pixel format fixes the rendering problem when using 0765 // texture buffers 0766 // BUG: 445561 0767 #ifdef Q_OS_MACOS 0768 m_texturesInfo.format = GL_RGBA; 0769 #else 0770 m_texturesInfo.format = GL_BGRA; 0771 #endif 0772 destinationColorDepthId = Integer16BitsColorDepthID; 0773 dbgUI << "Using 16 bits rgba"; 0774 #else 0775 KIS_ASSERT_X(false, 0776 "KisOpenGLCanvas2::updateTextureFormat", 0777 "Unexpected KisOpenGL::hasOpenGLES returned false"); 0778 #endif 0779 } else { 0780 #ifdef QT_OPENGL_ES_2_ANGLE 0781 // If OpenGL ES, fall back to 16-bit float -- supports HDR 0782 // Angle does ship GL_EXT_texture_norm16 but it doesn't seem 0783 // to be renderable by DXGI - it returns a pixel size of 0 0784 initializeRGBA16FTextures(ctx, m_texturesInfo, destinationColorDepthId); 0785 #else 0786 if (ctx->hasExtension("GL_EXT_texture_norm16")) { 0787 m_texturesInfo.internalFormat = GL_RGBA16_EXT; 0788 m_texturesInfo.type = GL_UNSIGNED_SHORT; 0789 m_texturesInfo.format = GL_RGBA; 0790 destinationColorDepthId = Integer16BitsColorDepthID; 0791 dbgUI << "Using 16 bits rgba (GLES v2)"; 0792 } 0793 #endif 0794 } 0795 } 0796 } 0797 else { 0798 // We will convert the colorspace to 16 bits rgba, instead of 8 bits 0799 if (colorDepthId == Integer16BitsColorDepthID) { 0800 if (!KisOpenGL::hasOpenGLES()) { 0801 #ifndef QT_OPENGL_ES_2 0802 m_texturesInfo.internalFormat = GL_RGBA16; 0803 m_texturesInfo.type = GL_UNSIGNED_SHORT; 0804 // On arm M1, GL_BGRA format is not aligned properly at the driver 0805 // changing the pixel format fixes the rendering problem when using 0806 // texture buffers 0807 // BUG: 445561 0808 #ifdef Q_OS_MACOS 0809 m_texturesInfo.format = GL_RGBA; 0810 #else 0811 m_texturesInfo.format = GL_BGRA; 0812 #endif 0813 destinationColorDepthId = Integer16BitsColorDepthID; 0814 dbgUI << "Using conversion to 16 bits rgba"; 0815 #else 0816 KIS_ASSERT_X(false, "KisOpenGLCanvas2::updateTextureFormat", 0817 "Unexpected KisOpenGL::hasOpenGLES returned false"); 0818 #endif 0819 } else { 0820 #ifdef QT_OPENGL_ES_2_ANGLE 0821 // If OpenGL ES, fall back to 16-bit float -- supports HDR 0822 // Angle does ship GL_EXT_texture_norm16 but it doesn't seem 0823 // to be renderable by DXGI - it returns a pixel size of 0 0824 initializeRGBA16FTextures(ctx, m_texturesInfo, destinationColorDepthId); 0825 #else 0826 if (ctx->hasExtension("GL_EXT_texture_norm16")) { 0827 m_texturesInfo.internalFormat = GL_RGBA16_EXT; 0828 m_texturesInfo.type = GL_UNSIGNED_SHORT; 0829 m_texturesInfo.format = GL_RGBA; 0830 destinationColorDepthId = Integer16BitsColorDepthID; 0831 dbgUI << "Using conversion to 16 bits rgba (GLES v2)"; 0832 } 0833 #endif 0834 } 0835 } else if (colorDepthId == Float16BitsColorDepthID) { 0836 initializeRGBA16FTextures(ctx, m_texturesInfo, destinationColorDepthId); 0837 } 0838 } 0839 0840 if (!m_internalColorManagementActive && 0841 colorModelId != destinationColorModelId) { 0842 0843 KisConfig cfg(false); 0844 KisConfig::OcioColorManagementMode cm = cfg.ocioColorManagementMode(); 0845 0846 if (cm != KisConfig::INTERNAL) { 0847 emit sigShowFloatingMessage( 0848 i18n("OpenColorIO is disabled: image color space is not supported"), 5000, true); 0849 } 0850 0851 warnUI << "WARNING: Internal color management was forcibly enabled"; 0852 warnUI << "Color Management Mode: " << cm; 0853 warnUI << ppVar(m_image->colorSpace()); 0854 warnUI << ppVar(destinationColorModelId); 0855 warnUI << ppVar(destinationColorDepthId); 0856 0857 cfg.setOcioColorManagementMode(KisConfig::INTERNAL); 0858 m_internalColorManagementActive = true; 0859 } 0860 0861 const KoColorProfile *profile = 0862 m_internalColorManagementActive || 0863 colorModelId != destinationColorModelId ? 0864 m_monitorProfile : m_image->colorSpace()->profile(); 0865 0866 /** 0867 * TODO: add an optimization so that the tile->convertTo() method 0868 * would not be called when not needed (DK) 0869 */ 0870 0871 const KoColorSpace *tilesDestinationColorSpace = 0872 KoColorSpaceRegistry::instance()->colorSpace(destinationColorModelId.id(), 0873 destinationColorDepthId.id(), 0874 profile); 0875 0876 m_updateInfoBuilder.setConversionOptions( 0877 ConversionOptions(tilesDestinationColorSpace, 0878 m_renderingIntent, 0879 m_conversionFlags)); 0880 } 0881