Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_opengl_canvas2.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 2006-2013 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2015 Michael Abrahams <miabraha@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#define GL_GLEXT_PROTOTYPES
9
10#include <QWindow>
11
16
17#include "canvas/kis_canvas2.h"
19#include "kis_config.h"
20#include "kis_config_notifier.h"
21#include "kis_debug.h"
22#include <KisViewManager.h>
23#include "KisRepaintDebugger.h"
24
25#include "KisOpenGLModeProber.h"
28
29#include "config-qt-patches-present.h"
30
31static bool OPENGL_SUCCESS = false;
32
35{
36 friend class KisOpenGLCanvas2;
40 ~CanvasBridge() override = default;
41 Q_DISABLE_COPY(CanvasBridge)
43protected:
44 KisCanvas2 *canvas() const override {
45 return m_canvas->canvas();
46 }
47 QOpenGLContext *openglContext() const override {
48 return m_canvas->context();
49 }
50 qreal devicePixelRatioF() const override {
51 return m_canvas->devicePixelRatioF();
52 }
56 QColor borderColor() const override {
57 return m_canvas->borderColor();
58 }
59 GLenum internalTextureFormat() const override {
60 return m_canvas->textureFormat();
61 }
62};
63
65{
66public:
68 delete renderer;
69 }
70
71 boost::optional<QRect> updateRect;
72#if KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
73 bool shouldSkipRenderingPass = false;
74#endif
77 QScopedPointer<KisOpenGLSync> glSyncObject;
79};
80
82 KisCoordinatesConverter *coordinatesConverter,
83 QWidget *parent,
84 KisImageWSP image,
85 const KisDisplayConfig &displayConfig,
87 BitDepthMode bitDepthRequest)
88 : QOpenGLWidget(parent)
89 , KisCanvasWidgetBase(canvas, coordinatesConverter)
90 , d(new Private())
91{
92 setProperty("krita_skip_srgb_surface_manager_assignment", true);
93
94 KisConfig cfg(false);
95 cfg.setCanvasState("OPENGL_STARTED");
96
97 d->renderer = new KisOpenGLCanvasRenderer(new CanvasBridge(this), image, displayConfig, displayFilter);
98
100 SIGNAL(sigShowFloatingMessage(QString, int, bool)),
101 SLOT(slotShowFloatingMessage(QString, int, bool)));
102
103 setAcceptDrops(true);
104 setAutoFillBackground(false);
105
106 setFocusPolicy(Qt::StrongFocus);
107 setAttribute(Qt::WA_NoSystemBackground, true);
108#ifdef Q_OS_MACOS
109 setAttribute(Qt::WA_AcceptTouchEvents, false);
110#else
111 setAttribute(Qt::WA_AcceptTouchEvents, true);
112#endif
113 setAttribute(Qt::WA_InputMethodEnabled, true);
114 setAttribute(Qt::WA_DontCreateNativeAncestors, true);
115
116 const bool osManagedSurfacePresent = KisPlatformPluginInterfaceFactory::instance()->surfaceColorManagedByOS();
117 bool useNativeSurfaceForCanvas = osManagedSurfacePresent && cfg.enableCanvasSurfaceColorSpaceManagement();
118 if (qEnvironmentVariableIsSet("KRITA_USE_NATIVE_CANVAS_SURFACE")) {
119 useNativeSurfaceForCanvas = qEnvironmentVariableIntValue("KRITA_USE_NATIVE_CANVAS_SURFACE");
120 qDebug() << "FPS-DEBUG: Krita canvas mode is overridden:" << (useNativeSurfaceForCanvas ? "native surface" : "legacy mode") << useNativeSurfaceForCanvas << qEnvironmentVariableIsSet("KRITA_USE_NATIVE_CANVAS_SURFACE");
121 }
122
123 if (useNativeSurfaceForCanvas) {
124 setAttribute(Qt::WA_NativeWindow, true);
125 }
126
127 setUpdateBehavior(PartialUpdate);
128
129 // we should make sure the texture doesn't have alpha channel,
130 // otherwise blending will not work correctly.
131 if (KisOpenGLModeProber::instance()->useHDRMode()) {
132 setTextureFormat(GL_RGBA16F);
133 } else {
134 if (bitDepthRequest == BitDepthMode::Depth10Bit) {
135 if (QSurfaceFormat::defaultFormat().redBufferSize() < 10) {
136 warnOpenGL <<
137 "WARNING: KisOpenGLCanvas2 was created with a 10-bit surface, "
138 "while the global surface format is still set to 8-bit. Expect "
139 "color banding to appear";
140 }
141 setTextureFormat(GL_RGB10_A2);
142 } else {
152 if (!KisOpenGL::hasOpenGLES()) {
153 setTextureFormat(GL_RGB8);
154 }
155 }
156 }
157
158 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
159 connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotPixelGridModeChanged()));
160
161 connect(canvas->viewManager()->canvasResourceProvider(), SIGNAL(sigEffectiveCompositeOpChanged()), SLOT(slotUpdateCursorColor()));
162 connect(canvas->viewManager()->canvasResourceProvider(), SIGNAL(sigPaintOpPresetChanged(KisPaintOpPresetSP)), SLOT(slotUpdateCursorColor()));
163
166 cfg.writeEntry("canvasState", "OPENGL_SUCCESS");
167}
168
170{
182 makeCurrent();
183
184 delete d;
185
186 doneCurrent();
187}
188
190{
191 KisOpenGLContextSwitchLockSkipOnQt5 contextLock(this);
192 d->renderer->setDisplayFilter(displayFilter);
193}
194
196{
197 KisOpenGLContextSwitchLockSkipOnQt5 contextLock(this);
199}
200
206
211
217
222
228
229void KisOpenGLCanvas2::resizeGL(int width, int height)
230{
231 d->renderer->resizeGL(width, height);
232 d->canvasImageDirtyRect = QRect(0, 0, width, height);
233}
234
236{
237#if KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
238 if (d->shouldSkipRenderingPass) {
239 return;
240 }
241#endif
242
243 const QRect updateRect = d->updateRect ? *d->updateRect : QRect();
244
245 if (!OPENGL_SUCCESS) {
246 KisConfig cfg(false);
247 cfg.writeEntry("canvasState", "OPENGL_PAINT_STARTED");
248 }
249
251 QRect canvasImageDirtyRect = d->canvasImageDirtyRect & rect();
252 d->canvasImageDirtyRect = QRect();
253 d->renderer->paintCanvasOnly(canvasImageDirtyRect, updateRect);
254 {
255 QPainter gc(this);
256 if (!updateRect.isEmpty()) {
257 gc.setClipRect(updateRect);
258 }
259
260 QRect decorationsBoundingRect = coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect();
261
262 if (!updateRect.isEmpty()) {
263 decorationsBoundingRect &= updateRect;
264 }
265
266 drawDecorations(gc, decorationsBoundingRect);
267 }
268
269 d->repaintDbg.paint(this, updateRect.isEmpty() ? rect() : updateRect);
270
271 // We create the glFenceSync object here instead of in KisOpenGLRenderer,
272 // because the glFenceSync object should be created after all render
273 // commands in a frame, not just the OpenGL canvas itself. Putting it
274 // outside of KisOpenGLRenderer allows the canvas widget to do extra
275 // rendering, which a QtQuick2-based canvas will need.
276 d->glSyncObject.reset(new KisOpenGLSync());
277
278 if (!OPENGL_SUCCESS) {
279 KisConfig cfg(false);
280 cfg.writeEntry("canvasState", "OPENGL_SUCCESS");
281 OPENGL_SUCCESS = true;
282 }
283}
284
286{
288
289 if (qFuzzyCompare(devicePixelRatioF(), qRound(devicePixelRatioF()))) {
297 d->updateRect = e->rect();
298 } else {
299 d->updateRect = this->rect();
300 }
301
302#if KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
308 if (isBusy()) {
309 //qWarning() << "WARNING: paint event delivered with the canvas non-ready, rescheduling...";
310 d->shouldSkipRenderingPass = true;
311 QOpenGLWidget::paintEvent(e);
312 d->shouldSkipRenderingPass = false;
313 QTimer::singleShot(0, this,
314 [this, updateRect = *d->updateRect] () {
315 if (updateRect.isEmpty()) {
316 this->update();
317 } else {
318 this->update(updateRect);
319 }
320 });
321 } else
322#endif
323 {
324 QOpenGLWidget::paintEvent(e);
325 }
326
327 d->updateRect = boost::none;
328}
329
331{
340 const QRect updateRect = d->updateRect ? *d->updateRect : QRect();
341
342 d->renderer->paintToolOutline(path, updateRect, thickness);
343}
344
346{
347 const bool isBusyStatus = d->glSyncObject && !d->glSyncObject->isSignaled();
349 return isBusyStatus;
350}
351
356
363
370
375
376void KisOpenGLCanvas2::slotShowFloatingMessage(const QString &message, int timeout, bool priority)
377{
378 canvas()->imageView()->showFloatingMessage(message, QIcon(), timeout, priority ? KisFloatingMessage::High : KisFloatingMessage::Medium);
379}
380
381QVariant KisOpenGLCanvas2::inputMethodQuery(Qt::InputMethodQuery query) const
382{
383 return processInputMethodQuery(query);
384}
385
386void KisOpenGLCanvas2::inputMethodEvent(QInputMethodEvent *event)
387{
389}
390
391void KisOpenGLCanvas2::focusInEvent(QFocusEvent *event)
392{
393 processFocusInEvent(event);
394}
395
396void KisOpenGLCanvas2::focusOutEvent(QFocusEvent *event)
397{
399}
400
402{
403 QOpenGLWidget::hideEvent(e);
405}
406
408{
409 QOpenGLWidget::showEvent(e);
411}
412
414{
415 KisOpenGLContextSwitchLockSkipOnQt5 contextLock(this);
416 d->renderer->setDisplayConfig(config);
417}
418
419void KisOpenGLCanvas2::channelSelectionChanged(const QBitArray &channelFlags)
420{
421 d->renderer->channelSelectionChanged(channelFlags);
422}
423
424
426{
427 KisOpenGLContextSwitchLockSkipOnQt5 contextLock(this);
429}
430
435
436
441
443{
444 KisOpenGLContextSwitchLockSkipOnQt5 contextLock(this);
446}
447
448void KisOpenGLCanvas2::updateCanvasImage(const QRect &imageUpdateRect)
449{
450 d->canvasImageDirtyRect |= imageUpdateRect;
451 update(imageUpdateRect);
452}
453
454void KisOpenGLCanvas2::updateCanvasDecorations(const QRect &decoUpdateRect)
455{
456 update(decoUpdateRect);
457}
459{
460 return focusNextPrevChild(next);
461}
462
467
469{
470 return
471 textureFormat() == GL_RGB10_A2 &&
472 format().redBufferSize() == 10 &&
473 format().greenBufferSize() == 10 &&
474 format().blueBufferSize() == 10 ?
477}
478
480 QString report;
481 QDebug str(&report);
482
483 str << "Texture Format: " << Qt::hex << Qt::showbase << textureFormat() << Qt::reset;
484
485 switch (textureFormat()) {
486 case GL_RGB10_A2:
487 str << " (" << "GL_RGB10_A2" << ")";
488 break;
489// not available in openGLES
490#ifdef GL_RGB10
491 case GL_RGB10:
492 str << " (" << "GL_RGB10" << ")";
493 break;
494#endif /* GL_RGB10 */
495// not available in openGLES
496#ifdef GL_RGB12
497 case GL_RGB12:
498 str << " (" << "GL_RGB12" << ")";
499 break;
500#endif /* GL_RGB12 */
501// not available in openGLES
502#ifdef GL_RGBA16
503 case GL_RGBA16:
504 str << " (" << "GL_RGBA16" << ")";
505 break;
506#endif /* GL_RGBA16 */
507// not available in openGLES
508#ifdef GL_RGB16
509 case GL_RGB16:
510 str << " (" << "GL_RGB16" << ")";
511 break;
512#endif /* GL_RGB16 */
513 case GL_RGBA16F:
514 str << " (" << "GL_RGBA16F" << ")";
515 break;
516 case GL_RGB8:
517 str << " (" << "GL_RGB8" << ")";
518 break;
519 case GL_RGBA8:
520 str << " (" << "GL_RGBA8" << ")";
521 break;
522 default:
523 str << " (" << "<unknown>" << ")";
524 break;
525 }
526 str << Qt::endl;
527
528 str << "FBO Buffer Size: "
529 << "R: " << format().redBufferSize() << " "
530 << "G: " << format().greenBufferSize() << " "
531 << "B: " << format().blueBufferSize() << " "
532 << "A: " << format().alphaBufferSize() << Qt::endl;
533
534 QWindow *win = windowHandle();
535 if (win) {
536 str << "Window Buffer Size: "
537 << "R: " << win->format().redBufferSize() << " "
538 << "G: " << win->format().greenBufferSize() << " "
539 << "B: " << win->format().blueBufferSize() << " "
540 << "A: " << win->format().alphaBufferSize() << Qt::endl;
541 }
542
543 if (win) {
544 str << "Global Buffer Size: "
545 << "R: " << QSurfaceFormat::defaultFormat().redBufferSize() << " "
546 << "G: " << QSurfaceFormat::defaultFormat().greenBufferSize() << " "
547 << "B: " << QSurfaceFormat::defaultFormat().blueBufferSize() << " "
548 << "A: " << QSurfaceFormat::defaultFormat().alphaBufferSize() << Qt::endl;
549 }
550
551 return report;
552}
float value(const T *src, size_t ch)
WrapAroundAxis
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
KisViewManager * viewManager() const
QPointer< KisView > imageView() const
void drawDecorations(QPainter &gc, const QRect &updateWidgetRect) const override
KisCoordinatesConverter * coordinatesConverter() const
QVariant processInputMethodQuery(Qt::InputMethodQuery query) const
void processFocusInEvent(QFocusEvent *event)
void processInputMethodEvent(QInputMethodEvent *event)
void processFocusOutEvent(QFocusEvent *event)
KisCanvas2 * canvas() const
void notifyDecorationsWindowMinimized(bool minimized)
QVector< QRect > updateCanvasProjection(const QVector< KisUpdateInfoSP > &infoObjects) override
static KisConfigNotifier * instance()
void setCanvasState(const QString &state) const
void writeEntry(const QString &name, const T &value)
Definition kis_config.h:779
bool enableCanvasSurfaceColorSpaceManagement(bool defaultValue=false) const
KisDisplayConfig This class keeps track of the color management configuration for image to display....
QColor borderColor() const override
CanvasBridge(KisOpenGLCanvas2 *canvas)
KisCanvas2 * canvas() const override
qreal devicePixelRatioF() const override
GLenum internalTextureFormat() const override
QOpenGLContext * openglContext() const override
KisCoordinatesConverter * coordinatesConverter() const override
~CanvasBridge() override=default
void showEvent(QShowEvent *event) override
void initializeGL() override
QString currentBitDepthUserReport() const override
BitDepthMode currentBitDepthMode() const override
void setDisplayConfig(const KisDisplayConfig &config) override
void setDisplayFilter(QSharedPointer< KisDisplayFilter > displayFilter) override
set the specified display filter on the canvas
void updateCanvasDecorations(const QRect &decoUpdateRect) override
void slotShowFloatingMessage(const QString &message, int timeout, bool priority)
void notifyImageColorSpaceChanged(const KoColorSpace *cs) override
set/update the color space of the attached image
bool wrapAroundViewingMode() const override
void inputMethodEvent(QInputMethodEvent *event) override
QVariant inputMethodQuery(Qt::InputMethodQuery query) const override
KisUpdateInfoSP startUpdateCanvasProjection(const QRect &rc) override
void paintGL() override
void setLodResetInProgress(bool value) override
void updateCanvasImage(const QRect &imageUpdateRect) override
bool isBusy() const override
KisOpenGLImageTexturesSP openGLImageTextures() const
void channelSelectionChanged(const QBitArray &channelFlags) override
void hideEvent(QHideEvent *event) override
KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget *parent, KisImageWSP image, const KisDisplayConfig &displayConfig, QSharedPointer< KisDisplayFilter > displayFilter, BitDepthMode bitDepthRequest)
void paintToolOutline(const KisOptimizedBrushOutline &path, int thickness=1)
QRect updateCanvasProjection(KisUpdateInfoSP info) override
void setWrapAroundViewingMode(bool value) override
void focusOutEvent(QFocusEvent *event) override
void resizeGL(int width, int height) override
void focusInEvent(QFocusEvent *event) override
WrapAroundAxis wrapAroundViewingModeAxis() const override
void setWrapAroundViewingModeAxis(WrapAroundAxis value) override
void paintEvent(QPaintEvent *e) override
bool callFocusNextPrevChild(bool next) override
To be implemented by the derived canvas.
void finishResizingImage(qint32 w, qint32 h) override
QRect updateCanvasProjection(KisUpdateInfoSP info)
void setDisplayFilter(QSharedPointer< KisDisplayFilter > displayFilter)
void resizeGL(int width, int height)
KisOpenGLImageTexturesSP openGLImageTextures() const
void channelSelectionChanged(const QBitArray &channelFlags)
KisUpdateInfoSP startUpdateCanvasProjection(const QRect &rc)
void paintCanvasOnly(const QRect &canvasImageDirtyRect, const QRect &viewportUpdateRect=QRect())
void setDisplayConfig(const KisDisplayConfig &config)
WrapAroundAxis wrapAroundViewingModeAxis() const
void setWrapAroundViewingModeAxis(WrapAroundAxis value)
void finishResizingImage(qint32 w, qint32 h)
void paintToolOutline(const KisOptimizedBrushOutline &path, const QRect &viewportUpdateRect, const int thickness=1)
void notifyImageColorSpaceChanged(const KoColorSpace *cs)
static KisOpenGLModeProber * instance()
static void init(QOpenGLContext *ctx)
static bool hasOpenGLES()
static KisOpenglCanvasDebugger * instance()
static KisPlatformPluginInterfaceFactory * instance()
void paint(QPaintDevice *paintDevice, const QRect &widgetRect)
KisCanvasResourceProvider * canvasResourceProvider()
static bool qFuzzyCompare(half p1, half p2)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define warnOpenGL
Definition kis_debug.h:102
static bool OPENGL_SUCCESS
boost::optional< QRect > updateRect
QScopedPointer< KisOpenGLSync > glSyncObject
KisOpenGLCanvasRenderer * renderer