File indexing completed on 2024-12-22 04:12:48
0001 /* This file is part of the KDE project 0002 * SPDX-FileCopyrightText: 2006-2013 Boudewijn Rempt <boud@valdyas.org> 0003 * SPDX-FileCopyrightText: 2015 Michael Abrahams <miabraha@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #define GL_GLEXT_PROTOTYPES 0009 0010 #include "opengl/KisOpenGLCanvasRenderer.h" 0011 0012 #include "kis_algebra_2d.h" 0013 #include "opengl/kis_opengl_shader_loader.h" 0014 #include "canvas/kis_canvas2.h" 0015 #include "canvas/kis_coordinates_converter.h" 0016 #include "canvas/kis_display_filter.h" 0017 #include "canvas/kis_display_color_converter.h" 0018 #include "canvas/kis_canvas_widget_base.h" 0019 #include "KisOpenGLModeProber.h" 0020 #include "kis_canvas_resource_provider.h" 0021 #include "kis_config.h" 0022 #include "kis_debug.h" 0023 0024 #include <QPainter> 0025 #include <QPainterPath> 0026 #include <QOpenGLPaintDevice> 0027 #include <QPointF> 0028 #include <QPointer> 0029 #include <QMatrix> 0030 #include <QTransform> 0031 #include <QThread> 0032 #include <QFile> 0033 #include <QOpenGLShaderProgram> 0034 #include <QOpenGLVertexArrayObject> 0035 #include <QOpenGLBuffer> 0036 #include <QOpenGLFramebufferObject> 0037 #include <QOpenGLFramebufferObjectFormat> 0038 #include <QMessageBox> 0039 #include <KoCompositeOpRegistry.h> 0040 #include <KoColorModelStandardIds.h> 0041 #include "KisOpenGLBufferCircularStorage.h" 0042 #include "kis_painting_tweaks.h" 0043 #include <KisOptimizedBrushOutline.h> 0044 0045 #include <config-ocio.h> 0046 0047 #define NEAR_VAL -1000.0 0048 #define FAR_VAL 1000.0 0049 0050 #ifndef GL_CLAMP_TO_EDGE 0051 #define GL_CLAMP_TO_EDGE 0x812F 0052 #endif 0053 0054 #define PROGRAM_VERTEX_ATTRIBUTE 0 0055 #define PROGRAM_TEXCOORD_ATTRIBUTE 1 0056 0057 // These buffers are used only for painting checkers, 0058 // so we can keep the number really low 0059 static constexpr int NumberOfBuffers = 2; 0060 0061 struct KisOpenGLCanvasRenderer::Private 0062 { 0063 public: 0064 ~Private() { 0065 delete displayShader; 0066 delete checkerShader; 0067 delete solidColorShader; 0068 0069 delete canvasBridge; 0070 } 0071 0072 bool canvasInitialized{false}; 0073 0074 KisOpenGLImageTexturesSP openGLImageTextures; 0075 0076 KisOpenGLShaderLoader shaderLoader; 0077 KisShaderProgram *displayShader{0}; 0078 KisShaderProgram *checkerShader{0}; 0079 KisShaderProgram *solidColorShader{0}; 0080 0081 QScopedPointer<QOpenGLFramebufferObject> canvasFBO; 0082 0083 bool displayShaderCompiledWithDisplayFilterSupport{false}; 0084 0085 GLfloat checkSizeScale; 0086 bool scrollCheckers; 0087 0088 QSharedPointer<KisDisplayFilter> displayFilter; 0089 KisOpenGL::FilterMode filterMode; 0090 bool proofingConfigIsUpdated=false; 0091 0092 bool wrapAroundMode{false}; 0093 WrapAroundAxis wrapAroundModeAxis{WRAPAROUND_BOTH}; 0094 0095 // Stores a quad for drawing the canvas 0096 QOpenGLVertexArrayObject quadVAO; 0097 0098 KisOpenGLBufferCircularStorage checkersVertexBuffer; 0099 KisOpenGLBufferCircularStorage checkersTextureVertexBuffer; 0100 0101 // Stores data for drawing tool outlines 0102 QOpenGLVertexArrayObject outlineVAO; 0103 QOpenGLBuffer lineVertexBuffer; 0104 0105 QVector3D vertices[6]; 0106 QVector2D texCoords[6]; 0107 0108 qreal pixelGridDrawingThreshold; 0109 bool pixelGridEnabled; 0110 QColor gridColor; 0111 QColor cursorColor; 0112 0113 bool lodSwitchInProgress = false; 0114 0115 CanvasBridge *canvasBridge; 0116 QSizeF pixelAlignedWidgetSize; 0117 QSize viewportDevicePixelSize; 0118 0119 int xToColWithWrapCompensation(int x, const QRect &imageRect) { 0120 int firstImageColumn = openGLImageTextures->xToCol(imageRect.left()); 0121 int lastImageColumn = openGLImageTextures->xToCol(imageRect.right()); 0122 0123 int colsPerImage = lastImageColumn - firstImageColumn + 1; 0124 int numWraps = floor(qreal(x) / imageRect.width()); 0125 int remainder = x - imageRect.width() * numWraps; 0126 0127 return colsPerImage * numWraps + openGLImageTextures->xToCol(remainder); 0128 } 0129 0130 int yToRowWithWrapCompensation(int y, const QRect &imageRect) { 0131 int firstImageRow = openGLImageTextures->yToRow(imageRect.top()); 0132 int lastImageRow = openGLImageTextures->yToRow(imageRect.bottom()); 0133 0134 int rowsPerImage = lastImageRow - firstImageRow + 1; 0135 int numWraps = floor(qreal(y) / imageRect.height()); 0136 int remainder = y - imageRect.height() * numWraps; 0137 0138 return rowsPerImage * numWraps + openGLImageTextures->yToRow(remainder); 0139 } 0140 0141 }; 0142 0143 KisOpenGLCanvasRenderer::KisOpenGLCanvasRenderer(CanvasBridge *canvasBridge, 0144 KisImageWSP image, 0145 KisDisplayColorConverter *colorConverter) 0146 : d(new Private()) 0147 { 0148 d->canvasBridge = canvasBridge; 0149 0150 d->openGLImageTextures = 0151 KisOpenGLImageTextures::getImageTextures(image, 0152 colorConverter->openGLCanvasSurfaceProfile(), 0153 colorConverter->renderingIntent(), 0154 colorConverter->conversionFlags()); 0155 0156 0157 setDisplayFilterImpl(colorConverter->displayFilter(), true); 0158 } 0159 0160 KisOpenGLCanvasRenderer::~KisOpenGLCanvasRenderer() 0161 { 0162 delete d; 0163 } 0164 0165 KisCanvas2 *KisOpenGLCanvasRenderer::canvas() const 0166 { 0167 return d->canvasBridge->canvas(); 0168 } 0169 0170 QOpenGLContext *KisOpenGLCanvasRenderer::context() const 0171 { 0172 return d->canvasBridge->openglContext(); 0173 } 0174 0175 qreal KisOpenGLCanvasRenderer::devicePixelRatioF() const 0176 { 0177 return d->canvasBridge->devicePixelRatioF(); 0178 } 0179 0180 KisCoordinatesConverter *KisOpenGLCanvasRenderer::coordinatesConverter() const 0181 { 0182 return d->canvasBridge->coordinatesConverter(); 0183 } 0184 0185 QColor KisOpenGLCanvasRenderer::borderColor() const 0186 { 0187 return d->canvasBridge->borderColor(); 0188 } 0189 0190 void KisOpenGLCanvasRenderer::setDisplayFilter(QSharedPointer<KisDisplayFilter> displayFilter) 0191 { 0192 setDisplayFilterImpl(displayFilter, false); 0193 } 0194 0195 void KisOpenGLCanvasRenderer::setDisplayFilterImpl(QSharedPointer<KisDisplayFilter> displayFilter, bool initializing) 0196 { 0197 bool needsInternalColorManagement = 0198 !displayFilter || displayFilter->useInternalColorManagement(); 0199 0200 bool needsFullRefresh = d->openGLImageTextures->setInternalColorManagementActive(needsInternalColorManagement); 0201 0202 d->displayFilter = displayFilter; 0203 0204 if (!initializing && needsFullRefresh) { 0205 canvas()->startUpdateInPatches(canvas()->image()->bounds()); 0206 } 0207 else if (!initializing) { 0208 canvas()->updateCanvas(); 0209 } 0210 } 0211 0212 void KisOpenGLCanvasRenderer::notifyImageColorSpaceChanged(const KoColorSpace *cs) 0213 { 0214 // FIXME: on color space change the data is refetched multiple 0215 // times by different actors! 0216 0217 if (d->openGLImageTextures->setImageColorSpace(cs)) { 0218 canvas()->startUpdateInPatches(canvas()->image()->bounds()); 0219 } 0220 } 0221 0222 void KisOpenGLCanvasRenderer::setWrapAroundViewingMode(bool value) 0223 { 0224 d->wrapAroundMode = value; 0225 } 0226 0227 bool KisOpenGLCanvasRenderer::wrapAroundViewingMode() const 0228 { 0229 return d->wrapAroundMode; 0230 } 0231 0232 void KisOpenGLCanvasRenderer::setWrapAroundViewingModeAxis(WrapAroundAxis value) 0233 { 0234 d->wrapAroundModeAxis = value; 0235 } 0236 0237 WrapAroundAxis KisOpenGLCanvasRenderer::wrapAroundViewingModeAxis() const 0238 { 0239 return d->wrapAroundModeAxis; 0240 } 0241 0242 void KisOpenGLCanvasRenderer::initializeGL() 0243 { 0244 KisOpenGL::initializeContext(context()); 0245 initializeOpenGLFunctions(); 0246 0247 KisConfig cfg(true); 0248 d->openGLImageTextures->setProofingConfig(canvas()->proofingConfiguration()); 0249 d->openGLImageTextures->initGL(context()->functions()); 0250 d->openGLImageTextures->generateCheckerTexture(KisCanvasWidgetBase::createCheckersImage(cfg.checkSize())); 0251 0252 initializeShaders(); 0253 0254 // If we support OpenGL 3.0, then prepare our VAOs and VBOs for drawing 0255 if (KisOpenGL::supportsVAO()) { 0256 d->quadVAO.create(); 0257 d->quadVAO.bind(); 0258 0259 glEnableVertexAttribArray(PROGRAM_VERTEX_ATTRIBUTE); 0260 glEnableVertexAttribArray(PROGRAM_TEXCOORD_ATTRIBUTE); 0261 0262 d->checkersVertexBuffer.allocate(NumberOfBuffers, 6 * 3 * sizeof(float)); 0263 d->checkersTextureVertexBuffer.allocate(NumberOfBuffers, 6 * 2 * sizeof(float)); 0264 0265 // Create the outline buffer, this buffer will store the outlines of 0266 // tools and will frequently change data 0267 d->outlineVAO.create(); 0268 d->outlineVAO.bind(); 0269 0270 glEnableVertexAttribArray(PROGRAM_VERTEX_ATTRIBUTE); 0271 0272 // The outline buffer has a StreamDraw usage pattern, because it changes constantly 0273 d->lineVertexBuffer.create(); 0274 d->lineVertexBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw); 0275 d->lineVertexBuffer.bind(); 0276 glVertexAttribPointer(PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0, 0); 0277 } 0278 0279 d->canvasInitialized = true; 0280 } 0281 0282 /** 0283 * Loads all shaders and reports compilation problems 0284 */ 0285 void KisOpenGLCanvasRenderer::initializeShaders() 0286 { 0287 KIS_SAFE_ASSERT_RECOVER_RETURN(!d->canvasInitialized); 0288 0289 delete d->checkerShader; 0290 delete d->solidColorShader; 0291 d->checkerShader = 0; 0292 d->solidColorShader = 0; 0293 0294 try { 0295 d->checkerShader = d->shaderLoader.loadCheckerShader(); 0296 d->solidColorShader = d->shaderLoader.loadSolidColorShader(); 0297 } catch (const ShaderLoaderException &e) { 0298 reportFailedShaderCompilation(e.what()); 0299 } 0300 0301 initializeDisplayShader(); 0302 } 0303 0304 void KisOpenGLCanvasRenderer::initializeDisplayShader() 0305 { 0306 KIS_SAFE_ASSERT_RECOVER_RETURN(!d->canvasInitialized); 0307 0308 bool useHiQualityFiltering = d->filterMode == KisOpenGL::HighQualityFiltering; 0309 0310 delete d->displayShader; 0311 d->displayShader = 0; 0312 0313 try { 0314 d->displayShader = d->shaderLoader.loadDisplayShader(d->displayFilter, useHiQualityFiltering); 0315 d->displayShaderCompiledWithDisplayFilterSupport = d->displayFilter; 0316 } catch (const ShaderLoaderException &e) { 0317 reportFailedShaderCompilation(e.what()); 0318 } 0319 } 0320 0321 /** 0322 * Displays a message box telling the user that 0323 * shader compilation failed and turns off OpenGL. 0324 */ 0325 void KisOpenGLCanvasRenderer::reportFailedShaderCompilation(const QString &context) 0326 { 0327 KisConfig cfg(false); 0328 0329 qDebug() << "Shader Compilation Failure: " << context; 0330 // TODO: Should do something else when using QtQuick2 0331 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), 0332 i18n("Krita could not initialize the OpenGL canvas:\n\n%1\n\n Krita will disable OpenGL and close now.", context), 0333 QMessageBox::Close); 0334 0335 cfg.disableOpenGL(); 0336 cfg.setCanvasState("OPENGL_FAILED"); 0337 } 0338 0339 void KisOpenGLCanvasRenderer::updateSize(const QSize &viewportSize) 0340 { 0341 d->viewportDevicePixelSize = viewportSize; 0342 d->pixelAlignedWidgetSize = QSizeF(viewportSize) / devicePixelRatioF(); 0343 0344 // The widget size may be an integer but here we actually want to give 0345 // KisCoordinatesConverter the logical viewport size aligned to device 0346 // pixels. 0347 coordinatesConverter()->setCanvasWidgetSize(d->pixelAlignedWidgetSize); 0348 } 0349 0350 void KisOpenGLCanvasRenderer::resizeGL(int width, int height) 0351 { 0352 // This is how QOpenGLCanvas sets the FBO and the viewport size. If 0353 // devicePixelRatioF() is non-integral, the result is truncated. 0354 // *Correction*: The FBO size is actually rounded, but the glViewport call 0355 // uses integer truncation and that's what really matters. 0356 int viewportWidth = static_cast<int>(width * devicePixelRatioF()); 0357 int viewportHeight = static_cast<int>(height * devicePixelRatioF()); 0358 0359 updateSize(QSize(viewportWidth, viewportHeight)); 0360 0361 if (KisOpenGL::useFBOForToolOutlineRendering()) { 0362 QOpenGLFramebufferObjectFormat format; 0363 if (KisOpenGLModeProber::instance()->useHDRMode()) { 0364 format.setInternalTextureFormat(GL_RGBA16F); 0365 } 0366 d->canvasFBO.reset(new QOpenGLFramebufferObject(d->viewportDevicePixelSize, format)); 0367 } 0368 } 0369 0370 void KisOpenGLCanvasRenderer::paintCanvasOnly(const QRect &canvasImageDirtyRect, const QRect &viewportUpdateRect) 0371 { 0372 if (d->canvasFBO) { 0373 if (!canvasImageDirtyRect.isEmpty()) { 0374 d->canvasFBO->bind(); 0375 renderCanvasGL(canvasImageDirtyRect); 0376 d->canvasFBO->release(); 0377 } 0378 QRect blitRect; 0379 if (viewportUpdateRect.isEmpty()) { 0380 blitRect = QRect(QPoint(), d->viewportDevicePixelSize); 0381 } else { 0382 const QTransform scale = QTransform::fromScale(1.0, -1.0) * QTransform::fromTranslate(0, d->pixelAlignedWidgetSize.height()) * QTransform::fromScale(devicePixelRatioF(), devicePixelRatioF()); 0383 blitRect = scale.mapRect(QRectF(viewportUpdateRect)).toAlignedRect(); 0384 } 0385 QOpenGLFramebufferObject::blitFramebuffer(nullptr, blitRect, d->canvasFBO.data(), blitRect, GL_COLOR_BUFFER_BIT, GL_NEAREST); 0386 QOpenGLFramebufferObject::bindDefault(); 0387 } else { 0388 QRect fullUpdateRect = canvasImageDirtyRect | viewportUpdateRect; 0389 if (fullUpdateRect.isEmpty()) { 0390 fullUpdateRect = QRect(QPoint(), d->viewportDevicePixelSize); 0391 } 0392 renderCanvasGL(fullUpdateRect); 0393 } 0394 } 0395 0396 void KisOpenGLCanvasRenderer::paintToolOutline(const KisOptimizedBrushOutline &path, const QRect &viewportUpdateRect, const int thickness) 0397 { 0398 if (!d->solidColorShader->bind()) { 0399 return; 0400 } 0401 0402 const QSizeF &widgetSize = d->pixelAlignedWidgetSize; 0403 0404 // setup the mvp transformation 0405 QMatrix4x4 projectionMatrix; 0406 projectionMatrix.setToIdentity(); 0407 // FIXME: It may be better to have the projection in device pixel, but 0408 // this requires introducing a new coordinate system. 0409 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0, NEAR_VAL, FAR_VAL); 0410 0411 // Set view/projection matrices 0412 QMatrix4x4 modelMatrix(coordinatesConverter()->flakeToWidgetTransform()); 0413 modelMatrix.optimize(); 0414 modelMatrix = projectionMatrix * modelMatrix; 0415 d->solidColorShader->setUniformValue(d->solidColorShader->location(Uniform::ModelViewProjection), modelMatrix); 0416 0417 d->solidColorShader->setUniformValue( 0418 d->solidColorShader->location(Uniform::FragmentColor), 0419 QVector4D(d->cursorColor.redF(), d->cursorColor.greenF(), d->cursorColor.blueF(), 1.0f)); 0420 0421 glEnable(GL_BLEND); 0422 glBlendFuncSeparate(GL_ONE, GL_SRC_COLOR, GL_ONE, GL_ONE); 0423 glBlendEquationSeparate(GL_FUNC_SUBTRACT, GL_FUNC_ADD); 0424 0425 0426 if (!viewportUpdateRect.isEmpty()) { 0427 const QRect deviceUpdateRect = widgetToSurface(viewportUpdateRect).toAlignedRect(); 0428 glScissor(deviceUpdateRect.x(), deviceUpdateRect.y(), deviceUpdateRect.width(), deviceUpdateRect.height()); 0429 glEnable(GL_SCISSOR_TEST); 0430 } 0431 0432 // Paint the tool outline 0433 if (KisOpenGL::supportsVAO()) { 0434 d->outlineVAO.bind(); 0435 d->lineVertexBuffer.bind(); 0436 } 0437 0438 QVector<QVector3D> verticesBuffer; 0439 0440 if (thickness > 1) { 0441 // Because glLineWidth is not supported on all versions of OpenGL (or rather, 0442 // is limited to 1, as returned by GL_ALIASED_LINE_WIDTH_RANGE), 0443 // we'll instead generate mitered-triangles. 0444 0445 const qreal halfWidth = (thickness * 0.5) / devicePixelRatioF(); 0446 const qreal miterLimit = (5 * thickness) / devicePixelRatioF(); 0447 0448 for (auto it = path.begin(); it != path.end(); ++it) { 0449 const QPolygonF& polygon = *it; 0450 0451 if (KisAlgebra2D::maxDimension(polygon.boundingRect()) < 0.5 * thickness) { 0452 continue; 0453 } 0454 0455 int triangleCount = 0; 0456 verticesBuffer.clear(); 0457 const bool closed = polygon.isClosed(); 0458 0459 for( int i = 1; i < polygon.count(); i++) { 0460 bool adjustFirst = closed? true: i > 1; 0461 bool adjustSecond = closed? true: i + 1 < polygon.count(); 0462 0463 QPointF p1 = polygon.at(i - 1); 0464 QPointF p2 = polygon.at(i); 0465 QPointF normal = p2 - p1; 0466 normal = KisAlgebra2D::normalize(QPointF(-normal.y(), normal.x())); 0467 0468 QPointF c1 = p1 - (normal * halfWidth); 0469 QPointF c2 = p1 + (normal * halfWidth); 0470 QPointF c3 = p2 - (normal * halfWidth); 0471 QPointF c4 = p2 + (normal * halfWidth); 0472 0473 // Add miter 0474 if (adjustFirst) { 0475 QPointF pPrev = i >= 2 ? 0476 QPointF(polygon.at(i-2)) : 0477 QPointF(polygon.at(qMax(polygon.count() - 2, 0))); 0478 0479 pPrev = p1 - pPrev; 0480 0481 QPointF miter = 0482 KisAlgebra2D::normalize(normal + 0483 KisAlgebra2D::normalize( 0484 QPointF(-pPrev.y(), pPrev.x()))); 0485 0486 const qreal dot = KisAlgebra2D::dotProduct(miter, normal); 0487 0488 if (KisAlgebra2D::norm((miter * halfWidth) / dot) < miterLimit) { 0489 c1 = p1 + ((miter * -halfWidth) / dot); 0490 c2 = p1 + ((miter * halfWidth) / dot); 0491 } 0492 } 0493 0494 if (adjustSecond) { 0495 QPointF pNext = i + 1 < polygon.count()? QPointF(polygon.at(i+1)) 0496 : QPointF(polygon.at(qMin(polygon.count(), 1))); 0497 pNext = pNext - p2; 0498 QPointF miter = 0499 KisAlgebra2D::normalize( 0500 normal + KisAlgebra2D::normalize(QPointF(-pNext.y(), pNext.x()))); 0501 const qreal dot = KisAlgebra2D::dotProduct(miter, normal); 0502 0503 if (KisAlgebra2D::norm((miter * halfWidth) / dot) < miterLimit) { 0504 c3 = p2 + ((miter * -halfWidth) / dot); 0505 c4 = p2 + (miter * halfWidth) / dot; 0506 } 0507 } 0508 0509 verticesBuffer.append(QVector3D(c1)); 0510 verticesBuffer.append(QVector3D(c3)); 0511 verticesBuffer.append(QVector3D(c2)); 0512 verticesBuffer.append(QVector3D(c4)); 0513 verticesBuffer.append(QVector3D(c2)); 0514 verticesBuffer.append(QVector3D(c3)); 0515 triangleCount += 2; 0516 } 0517 0518 if (KisOpenGL::supportsVAO()) { 0519 d->lineVertexBuffer.bind(); 0520 d->lineVertexBuffer.allocate(verticesBuffer.constData(), 3 * verticesBuffer.size() * sizeof(float)); 0521 } 0522 else { 0523 d->solidColorShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); 0524 d->solidColorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, verticesBuffer.constData()); 0525 } 0526 0527 glDrawArrays(GL_TRIANGLES, 0, triangleCount * 3); 0528 } 0529 } else { 0530 // Convert every disjointed subpath to a polygon and draw that polygon 0531 for (auto it = path.begin(); it != path.end(); ++it) { 0532 const QPolygonF& polygon = *it; 0533 0534 if (KisAlgebra2D::maxDimension(polygon.boundingRect()) < 0.5) { 0535 continue; 0536 } 0537 0538 const int verticesCount = polygon.count(); 0539 0540 if (verticesBuffer.size() < verticesCount) { 0541 verticesBuffer.resize(verticesCount); 0542 } 0543 0544 for (int vertIndex = 0; vertIndex < verticesCount; vertIndex++) { 0545 QPointF point = polygon.at(vertIndex); 0546 verticesBuffer[vertIndex].setX(point.x()); 0547 verticesBuffer[vertIndex].setY(point.y()); 0548 } 0549 if (KisOpenGL::supportsVAO()) { 0550 d->lineVertexBuffer.bind(); 0551 d->lineVertexBuffer.allocate(verticesBuffer.constData(), 3 * verticesCount * sizeof(float)); 0552 } 0553 else { 0554 d->solidColorShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); 0555 d->solidColorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, verticesBuffer.constData()); 0556 } 0557 0558 0559 0560 glDrawArrays(GL_LINE_STRIP, 0, verticesCount); 0561 } 0562 } 0563 0564 if (KisOpenGL::supportsVAO()) { 0565 d->lineVertexBuffer.release(); 0566 d->outlineVAO.release(); 0567 } 0568 0569 if (!viewportUpdateRect.isEmpty()) { 0570 glDisable(GL_SCISSOR_TEST); 0571 } 0572 0573 glBlendEquation(GL_FUNC_ADD); 0574 glBlendFunc(GL_ONE, GL_ZERO); 0575 glDisable(GL_BLEND); 0576 0577 d->solidColorShader->release(); 0578 } 0579 0580 void KisOpenGLCanvasRenderer::setLodResetInProgress(bool value) 0581 { 0582 d->lodSwitchInProgress = value; 0583 } 0584 0585 void KisOpenGLCanvasRenderer::drawBackground(const QRect &updateRect) 0586 { 0587 Q_UNUSED(updateRect); 0588 0589 // Draw the border (that is, clear the whole widget to the border color) 0590 QColor widgetBackgroundColor = borderColor(); 0591 0592 const KoColorSpace *finalColorSpace = 0593 KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), 0594 d->openGLImageTextures->updateInfoBuilder().destinationColorSpace()->colorDepthId().id(), 0595 d->openGLImageTextures->monitorProfile()); 0596 0597 KoColor convertedBackgroundColor = KoColor(widgetBackgroundColor, KoColorSpaceRegistry::instance()->rgb8()); 0598 convertedBackgroundColor.convertTo(finalColorSpace); 0599 0600 QVector<float> channels = QVector<float>(4); 0601 convertedBackgroundColor.colorSpace()->normalisedChannelsValue(convertedBackgroundColor.data(), channels); 0602 0603 0604 // Data returned by KoRgbU8ColorSpace comes in the order: blue, green, red. 0605 glClearColor(channels[2], channels[1], channels[0], 1.0); 0606 glClear(GL_COLOR_BUFFER_BIT); 0607 } 0608 0609 void KisOpenGLCanvasRenderer::drawCheckers(const QRect &updateRect) 0610 { 0611 Q_UNUSED(updateRect); 0612 0613 if (!d->checkerShader) { 0614 return; 0615 } 0616 0617 KisCoordinatesConverter *converter = coordinatesConverter(); 0618 QTransform textureTransform; 0619 QTransform modelTransform; 0620 QRectF textureRect; 0621 QRectF modelRect; 0622 0623 const QSizeF &widgetSize = d->pixelAlignedWidgetSize; 0624 QRectF viewportRect; 0625 if (!d->wrapAroundMode) { 0626 viewportRect = converter->imageRectInViewportPixels(); 0627 } 0628 else { 0629 const QRectF ir = converter->imageRectInViewportPixels(); 0630 viewportRect = converter->widgetToViewport(QRectF(0, 0, widgetSize.width(), widgetSize.height())); 0631 if (d->wrapAroundModeAxis == WRAPAROUND_HORIZONTAL) { 0632 viewportRect.setTop(ir.top()); 0633 viewportRect.setBottom(ir.bottom()); 0634 } 0635 else if (d->wrapAroundModeAxis == WRAPAROUND_VERTICAL) { 0636 viewportRect.setLeft(ir.left()); 0637 viewportRect.setRight(ir.right()); 0638 } 0639 } 0640 0641 // TODO: check if it works correctly 0642 if (!canvas()->renderingLimit().isEmpty()) { 0643 const QRect vrect = converter->imageToViewport(canvas()->renderingLimit()).toAlignedRect(); 0644 viewportRect &= vrect; 0645 } 0646 0647 converter->getOpenGLCheckersInfo(viewportRect, 0648 &textureTransform, &modelTransform, &textureRect, &modelRect, d->scrollCheckers); 0649 0650 textureTransform *= QTransform::fromScale(d->checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE, 0651 d->checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE); 0652 0653 if (!d->checkerShader->bind()) { 0654 qWarning() << "Could not bind checker shader"; 0655 return; 0656 } 0657 0658 QMatrix4x4 projectionMatrix; 0659 projectionMatrix.setToIdentity(); 0660 // FIXME: It may be better to have the projection in device pixel, but 0661 // this requires introducing a new coordinate system. 0662 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0, NEAR_VAL, FAR_VAL); 0663 0664 // Set view/projection matrices 0665 QMatrix4x4 modelMatrix(modelTransform); 0666 modelMatrix.optimize(); 0667 modelMatrix = projectionMatrix * modelMatrix; 0668 d->checkerShader->setUniformValue(d->checkerShader->location(Uniform::ModelViewProjection), modelMatrix); 0669 0670 QMatrix4x4 textureMatrix(textureTransform); 0671 d->checkerShader->setUniformValue(d->checkerShader->location(Uniform::TextureMatrix), textureMatrix); 0672 0673 //Setup the geometry for rendering 0674 if (KisOpenGL::supportsVAO()) { 0675 KisPaintingTweaks::rectToVertices(d->vertices, modelRect); 0676 QOpenGLBuffer *vertexBuf = d->checkersVertexBuffer.getNextBuffer(); 0677 0678 vertexBuf->bind(); 0679 vertexBuf->write(0, d->vertices, 3 * 6 * sizeof(float)); 0680 glVertexAttribPointer(PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0, 0); 0681 0682 0683 KisPaintingTweaks::rectToTexCoords(d->texCoords, textureRect); 0684 QOpenGLBuffer *vertexTextureBuf = d->checkersTextureVertexBuffer.getNextBuffer(); 0685 0686 vertexTextureBuf->bind(); 0687 vertexTextureBuf->write(0, d->texCoords, 2 * 6 * sizeof(float)); 0688 glVertexAttribPointer(PROGRAM_TEXCOORD_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, 0); 0689 } 0690 else { 0691 KisPaintingTweaks::rectToVertices(d->vertices, modelRect); 0692 d->checkerShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); 0693 d->checkerShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices); 0694 0695 KisPaintingTweaks::rectToTexCoords(d->texCoords, textureRect); 0696 d->checkerShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); 0697 d->checkerShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords); 0698 } 0699 0700 // render checkers 0701 glActiveTexture(GL_TEXTURE0); 0702 glBindTexture(GL_TEXTURE_2D, d->openGLImageTextures->checkerTexture()); 0703 0704 glDrawArrays(GL_TRIANGLES, 0, 6); 0705 0706 glBindTexture(GL_TEXTURE_2D, 0); 0707 d->checkerShader->release(); 0708 glBindBuffer(GL_ARRAY_BUFFER, 0); 0709 } 0710 0711 void KisOpenGLCanvasRenderer::drawGrid(const QRect &updateRect) 0712 { 0713 if (!d->solidColorShader->bind()) { 0714 return; 0715 } 0716 0717 const QSizeF &widgetSize = d->pixelAlignedWidgetSize; 0718 0719 QMatrix4x4 projectionMatrix; 0720 projectionMatrix.setToIdentity(); 0721 // FIXME: It may be better to have the projection in device pixel, but 0722 // this requires introducing a new coordinate system. 0723 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0, NEAR_VAL, FAR_VAL); 0724 0725 // Set view/projection matrices 0726 QMatrix4x4 modelMatrix(coordinatesConverter()->imageToWidgetTransform()); 0727 modelMatrix.optimize(); 0728 modelMatrix = projectionMatrix * modelMatrix; 0729 d->solidColorShader->setUniformValue(d->solidColorShader->location(Uniform::ModelViewProjection), modelMatrix); 0730 0731 glEnable(GL_BLEND); 0732 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 0733 0734 d->solidColorShader->setUniformValue( 0735 d->solidColorShader->location(Uniform::FragmentColor), 0736 QVector4D(d->gridColor.redF(), d->gridColor.greenF(), d->gridColor.blueF(), 0.5f)); 0737 0738 QRectF widgetRect(0,0, widgetSize.width(), widgetSize.height()); 0739 QRectF widgetRectInImagePixels = coordinatesConverter()->documentToImage(coordinatesConverter()->widgetToDocument(widgetRect)); 0740 QRect wr = widgetRectInImagePixels.toAlignedRect(); 0741 0742 if (!d->wrapAroundMode) { 0743 wr &= d->openGLImageTextures->storedImageBounds(); 0744 } 0745 0746 if (!updateRect.isEmpty()) { 0747 const QRect updateRectInImagePixels = coordinatesConverter()->widgetToImage(updateRect).toAlignedRect(); 0748 wr &= updateRectInImagePixels; 0749 } 0750 0751 QPoint topLeftCorner = wr.topLeft(); 0752 QPoint bottomRightCorner = wr.bottomRight() + QPoint(1, 1); 0753 QVector<QVector3D> grid; 0754 0755 for (int i = topLeftCorner.x(); i <= bottomRightCorner.x(); ++i) { 0756 grid.append(QVector3D(i, topLeftCorner.y(), 0)); 0757 grid.append(QVector3D(i, bottomRightCorner.y(), 0)); 0758 } 0759 for (int i = topLeftCorner.y(); i <= bottomRightCorner.y(); ++i) { 0760 grid.append(QVector3D(topLeftCorner.x(), i, 0)); 0761 grid.append(QVector3D(bottomRightCorner.x(), i, 0)); 0762 } 0763 0764 if (KisOpenGL::supportsVAO()) { 0765 d->outlineVAO.bind(); 0766 d->lineVertexBuffer.bind(); 0767 d->lineVertexBuffer.allocate(grid.constData(), 3 * grid.size() * sizeof(float)); 0768 } 0769 else { 0770 d->solidColorShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); 0771 d->solidColorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, grid.constData()); 0772 } 0773 0774 glDrawArrays(GL_LINES, 0, grid.size()); 0775 0776 if (KisOpenGL::supportsVAO()) { 0777 d->lineVertexBuffer.release(); 0778 d->outlineVAO.release(); 0779 } 0780 0781 d->solidColorShader->release(); 0782 glDisable(GL_BLEND); 0783 } 0784 0785 void KisOpenGLCanvasRenderer::drawImage(const QRect &updateRect) 0786 { 0787 if (!d->displayShader) { 0788 return; 0789 } 0790 0791 glEnable(GL_BLEND); 0792 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 0793 0794 KisCoordinatesConverter *converter = coordinatesConverter(); 0795 0796 d->displayShader->bind(); 0797 0798 const QSizeF &widgetSize = d->pixelAlignedWidgetSize; 0799 0800 QMatrix4x4 textureMatrix; 0801 textureMatrix.setToIdentity(); 0802 d->displayShader->setUniformValue(d->displayShader->location(Uniform::TextureMatrix), textureMatrix); 0803 0804 QRectF widgetRect(0,0, widgetSize.width(), widgetSize.height()); 0805 0806 if (!updateRect.isEmpty()) { 0807 widgetRect &= updateRect; 0808 } 0809 0810 QRectF widgetRectInImagePixels = converter->documentToImage(converter->widgetToDocument(widgetRect)); 0811 0812 const QRect renderingLimit = canvas()->renderingLimit(); 0813 0814 if (!renderingLimit.isEmpty()) { 0815 widgetRectInImagePixels &= renderingLimit; 0816 } 0817 0818 qreal scaleX, scaleY; 0819 converter->imagePhysicalScale(&scaleX, &scaleY); 0820 0821 d->displayShader->setUniformValue(d->displayShader->location(Uniform::ViewportScale), (GLfloat) scaleX); 0822 d->displayShader->setUniformValue(d->displayShader->location(Uniform::TexelSize), (GLfloat) d->openGLImageTextures->texelSize()); 0823 0824 QRect ir = d->openGLImageTextures->storedImageBounds(); 0825 QRect wr = widgetRectInImagePixels.toAlignedRect(); 0826 0827 if (!d->wrapAroundMode) { 0828 // if we don't want to paint wrapping images, just limit the 0829 // processing area, and the code will handle all the rest 0830 wr &= ir; 0831 } 0832 else if (d->wrapAroundModeAxis == WRAPAROUND_HORIZONTAL) { 0833 wr.setTop(ir.top()); 0834 wr.setBottom(ir.bottom()); 0835 } 0836 else if (d->wrapAroundModeAxis == WRAPAROUND_VERTICAL) { 0837 wr.setLeft(ir.left()); 0838 wr.setRight(ir.right()); 0839 } 0840 0841 const int firstColumn = d->xToColWithWrapCompensation(wr.left(), ir); 0842 const int lastColumn = d->xToColWithWrapCompensation(wr.right(), ir); 0843 const int firstRow = d->yToRowWithWrapCompensation(wr.top(), ir); 0844 const int lastRow = d->yToRowWithWrapCompensation(wr.bottom(), ir); 0845 0846 const int minColumn = d->openGLImageTextures->xToCol(ir.left()); 0847 const int maxColumn = d->openGLImageTextures->xToCol(ir.right()); 0848 const int minRow = d->openGLImageTextures->yToRow(ir.top()); 0849 const int maxRow = d->openGLImageTextures->yToRow(ir.bottom()); 0850 0851 const int imageColumns = maxColumn - minColumn + 1; 0852 const int imageRows = maxRow - minRow + 1; 0853 0854 if (d->displayFilter) { 0855 d->displayFilter->setupTextures(this, d->displayShader); 0856 } 0857 0858 const int firstCloneX = qFloor(qreal(firstColumn) / imageColumns); 0859 const int lastCloneX = qFloor(qreal(lastColumn) / imageColumns); 0860 const int firstCloneY = qFloor(qreal(firstRow) / imageRows); 0861 const int lastCloneY = qFloor(qreal(lastRow) / imageRows); 0862 0863 for (int cloneY = firstCloneY; cloneY <= lastCloneY; cloneY++) { 0864 for (int cloneX = firstCloneX; cloneX <= lastCloneX; cloneX++) { 0865 0866 const int localFirstCol = cloneX == firstCloneX ? KisAlgebra2D::wrapValue(firstColumn, imageColumns) : 0; 0867 const int localLastCol = cloneX == lastCloneX ? KisAlgebra2D::wrapValue(lastColumn, imageColumns) : imageColumns - 1; 0868 0869 const int localFirstRow = cloneY == firstCloneY ? KisAlgebra2D::wrapValue(firstRow, imageRows) : 0; 0870 const int localLastRow = cloneY == lastCloneY ? KisAlgebra2D::wrapValue(lastRow, imageRows) : imageRows - 1; 0871 0872 drawImageTiles(localFirstCol, localLastCol, 0873 localFirstRow, localLastRow, 0874 scaleX, scaleY, QPoint(cloneX, cloneY)); 0875 } 0876 } 0877 0878 d->displayShader->release(); 0879 0880 glDisable(GL_BLEND); 0881 } 0882 0883 void KisOpenGLCanvasRenderer::drawImageTiles(int firstCol, int lastCol, int firstRow, int lastRow, qreal scaleX, qreal scaleY, const QPoint &wrapAroundOffset) 0884 { 0885 KisCoordinatesConverter *converter = coordinatesConverter(); 0886 const QSizeF &widgetSize = d->pixelAlignedWidgetSize; 0887 0888 QMatrix4x4 projectionMatrix; 0889 projectionMatrix.setToIdentity(); 0890 // FIXME: It may be better to have the projection in device pixel, but 0891 // this requires introducing a new coordinate system. 0892 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0, NEAR_VAL, FAR_VAL); 0893 0894 QTransform modelTransform = converter->imageToWidgetTransform(); 0895 0896 if (!wrapAroundOffset.isNull()) { 0897 const QRect ir = d->openGLImageTextures->storedImageBounds(); 0898 0899 const QTransform wrapAroundTranslate = QTransform::fromTranslate(ir.width() * wrapAroundOffset.x(), 0900 ir.height() * wrapAroundOffset.y()); 0901 modelTransform = wrapAroundTranslate * modelTransform; 0902 } 0903 0904 // Set view/projection matrices 0905 QMatrix4x4 modelMatrix(modelTransform); 0906 modelMatrix.optimize(); 0907 modelMatrix = projectionMatrix * modelMatrix; 0908 d->displayShader->setUniformValue(d->displayShader->location(Uniform::ModelViewProjection), modelMatrix); 0909 0910 int lastTileLodPlane = -1; 0911 0912 for (int col = firstCol; col <= lastCol; col++) { 0913 for (int row = firstRow; row <= lastRow; row++) { 0914 0915 KisTextureTile *tile = 0916 d->openGLImageTextures->getTextureTileCR(col, row); 0917 0918 if (!tile) { 0919 warnUI << "OpenGL: Trying to paint texture tile but it has not been created yet."; 0920 continue; 0921 } 0922 0923 //Setup the geometry for rendering 0924 if (KisOpenGL::supportsVAO()) { 0925 const int tileIndex = d->openGLImageTextures->getTextureBufferIndexCR(col, row); 0926 0927 const int vertexRectSize = 6 * 3 * sizeof(float); 0928 d->openGLImageTextures->tileVertexBuffer()->bind(); 0929 glVertexAttribPointer(PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<void*>(tileIndex * vertexRectSize)); 0930 0931 const int textureRectSize = 6 * 2 * sizeof(float); 0932 d->openGLImageTextures->tileTexCoordBuffer()->bind(); 0933 glVertexAttribPointer(PROGRAM_TEXCOORD_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<void*>(tileIndex * textureRectSize)); 0934 0935 } else { 0936 0937 const QRectF textureRect = tile->tileRectInTexturePixels(); 0938 const QRectF modelRect = tile->tileRectInImagePixels(); 0939 0940 KisPaintingTweaks::rectToVertices(d->vertices, modelRect); 0941 d->checkerShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); 0942 d->checkerShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices); 0943 0944 KisPaintingTweaks::rectToTexCoords(d->texCoords, textureRect); 0945 d->checkerShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); 0946 d->checkerShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords); 0947 } 0948 0949 glActiveTexture(GL_TEXTURE0); 0950 0951 // switching uniform is a rather expensive operation on macOS, so we change it only 0952 // when it is really needed 0953 const int currentLodPlane = tile->bindToActiveTexture(d->lodSwitchInProgress); 0954 if (d->displayShader->location(Uniform::FixedLodLevel) >= 0 && 0955 (lastTileLodPlane < 0 || lastTileLodPlane != currentLodPlane)) { 0956 0957 d->displayShader->setUniformValue(d->displayShader->location(Uniform::FixedLodLevel), 0958 (GLfloat) currentLodPlane); 0959 } 0960 0961 if (currentLodPlane > 0) { 0962 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); 0963 } else if (SCALE_MORE_OR_EQUAL_TO(scaleX, scaleY, 2.0)) { 0964 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 0965 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 0966 } else { 0967 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 0968 0969 switch(d->filterMode) { 0970 case KisOpenGL::NearestFilterMode: 0971 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 0972 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 0973 break; 0974 case KisOpenGL::BilinearFilterMode: 0975 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 0976 break; 0977 case KisOpenGL::TrilinearFilterMode: 0978 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 0979 break; 0980 case KisOpenGL::HighQualityFiltering: 0981 if (SCALE_LESS_THAN(scaleX, scaleY, 0.5)) { 0982 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); 0983 } else { 0984 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 0985 } 0986 break; 0987 } 0988 } 0989 0990 glDrawArrays(GL_TRIANGLES, 0, 6); 0991 } 0992 } 0993 0994 glBindTexture(GL_TEXTURE_2D, 0); 0995 glBindBuffer(GL_ARRAY_BUFFER, 0); 0996 } 0997 0998 void KisOpenGLCanvasRenderer::updateConfig() 0999 { 1000 KisConfig cfg(true); 1001 d->checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast<GLfloat>(cfg.checkSize()); 1002 d->scrollCheckers = cfg.scrollCheckers(); 1003 1004 d->openGLImageTextures->generateCheckerTexture(KisCanvasWidgetBase::createCheckersImage(cfg.checkSize())); 1005 d->openGLImageTextures->updateConfig(cfg.useOpenGLTextureBuffer(), cfg.numMipmapLevels()); 1006 d->filterMode = (KisOpenGL::FilterMode) cfg.openGLFilteringMode(); 1007 1008 updateCursorColor(); 1009 } 1010 1011 void KisOpenGLCanvasRenderer::updateCursorColor() 1012 { 1013 KisConfig cfg(true); 1014 bool useSeparateEraserCursor = cfg.separateEraserCursor() && 1015 canvas()->resourceManager()->resource(KoCanvasResource::CurrentEffectiveCompositeOp).toString() == COMPOSITE_ERASE; 1016 1017 d->cursorColor = (!useSeparateEraserCursor) ? cfg.getCursorMainColor() : cfg.getEraserCursorMainColor(); 1018 } 1019 1020 void KisOpenGLCanvasRenderer::updatePixelGridMode() 1021 { 1022 KisConfig cfg(true); 1023 1024 d->pixelGridDrawingThreshold = cfg.getPixelGridDrawingThreshold(); 1025 d->pixelGridEnabled = cfg.pixelGridEnabled(); 1026 d->gridColor = cfg.getPixelGridColor(); 1027 } 1028 1029 QRectF KisOpenGLCanvasRenderer::widgetToSurface(const QRectF &rc) 1030 { 1031 const qreal ratio = devicePixelRatioF(); 1032 1033 return QRectF(rc.x() * ratio, 1034 (d->pixelAlignedWidgetSize.height() - rc.y() - rc.height()) * ratio, 1035 rc.width() * ratio, 1036 rc.height() * ratio); 1037 } 1038 1039 QRectF KisOpenGLCanvasRenderer::surfaceToWidget(const QRectF &rc) 1040 { 1041 const qreal ratio = devicePixelRatioF(); 1042 1043 return QRectF(rc.x() / ratio, 1044 d->pixelAlignedWidgetSize.height() - (rc.y() + rc.height()) / ratio, 1045 rc.width() / ratio, 1046 rc.height() / ratio); 1047 } 1048 1049 1050 void KisOpenGLCanvasRenderer::renderCanvasGL(const QRect &updateRect) 1051 { 1052 if ((d->displayFilter && d->displayFilter->updateShader()) || 1053 (bool(d->displayFilter) != d->displayShaderCompiledWithDisplayFilterSupport)) { 1054 1055 KIS_SAFE_ASSERT_RECOVER_NOOP(d->canvasInitialized); 1056 1057 d->canvasInitialized = false; // TODO: check if actually needed? 1058 initializeDisplayShader(); 1059 d->canvasInitialized = true; 1060 } 1061 1062 if (KisOpenGL::supportsVAO()) { 1063 d->quadVAO.bind(); 1064 } 1065 1066 QRect alignedUpdateRect = updateRect; 1067 1068 if (!updateRect.isEmpty()) { 1069 const QRect deviceUpdateRect = widgetToSurface(updateRect).toAlignedRect(); 1070 alignedUpdateRect = surfaceToWidget(deviceUpdateRect).toAlignedRect(); 1071 1072 glScissor(deviceUpdateRect.x(), deviceUpdateRect.y(), deviceUpdateRect.width(), deviceUpdateRect.height()); 1073 glEnable(GL_SCISSOR_TEST); 1074 } 1075 1076 drawBackground(alignedUpdateRect); 1077 drawCheckers(alignedUpdateRect); 1078 drawImage(alignedUpdateRect); 1079 1080 if ((coordinatesConverter()->effectivePhysicalZoom() > d->pixelGridDrawingThreshold - 0.00001) && d->pixelGridEnabled) { 1081 drawGrid(alignedUpdateRect); 1082 } 1083 1084 if (!updateRect.isEmpty()) { 1085 glDisable(GL_SCISSOR_TEST); 1086 } 1087 1088 if (KisOpenGL::supportsVAO()) { 1089 d->quadVAO.release(); 1090 } 1091 } 1092 1093 void KisOpenGLCanvasRenderer::setDisplayColorConverter(KisDisplayColorConverter *colorConverter) 1094 { 1095 d->openGLImageTextures->setMonitorProfile(colorConverter->openGLCanvasSurfaceProfile(), 1096 colorConverter->renderingIntent(), 1097 colorConverter->conversionFlags()); 1098 } 1099 1100 void KisOpenGLCanvasRenderer::channelSelectionChanged(const QBitArray &channelFlags) 1101 { 1102 d->openGLImageTextures->setChannelFlags(channelFlags); 1103 } 1104 1105 1106 void KisOpenGLCanvasRenderer::finishResizingImage(qint32 w, qint32 h) 1107 { 1108 if (d->canvasInitialized) { 1109 d->openGLImageTextures->slotImageSizeChanged(w, h); 1110 } 1111 } 1112 1113 KisUpdateInfoSP KisOpenGLCanvasRenderer::startUpdateCanvasProjection(const QRect & rc) 1114 { 1115 if (canvas()->proofingConfigUpdated()) { 1116 d->openGLImageTextures->setProofingConfig(canvas()->proofingConfiguration()); 1117 canvas()->setProofingConfigUpdated(false); 1118 } 1119 return d->openGLImageTextures->updateCache(rc, d->openGLImageTextures->image()); 1120 } 1121 1122 1123 QRect KisOpenGLCanvasRenderer::updateCanvasProjection(KisUpdateInfoSP info) 1124 { 1125 // See KisQPainterCanvas::updateCanvasProjection for more info 1126 bool isOpenGLUpdateInfo = dynamic_cast<KisOpenGLUpdateInfo*>(info.data()); 1127 if (isOpenGLUpdateInfo) { 1128 d->openGLImageTextures->recalculateCache(info, d->lodSwitchInProgress); 1129 } 1130 1131 const QRect dirty = kisGrowRect(coordinatesConverter()->imageToWidget(info->dirtyImageRect()).toAlignedRect(), 2); 1132 return dirty; 1133 } 1134 1135 KisOpenGLImageTexturesSP KisOpenGLCanvasRenderer::openGLImageTextures() const 1136 { 1137 return d->openGLImageTextures; 1138 }