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
26#include "KisOpenGLModeProber.h"
29
30#include "config-qt-patches-present.h"
31
32static bool OPENGL_SUCCESS = false;
33
36{
37 friend class KisOpenGLCanvas2;
41 ~CanvasBridge() override = default;
42 Q_DISABLE_COPY(CanvasBridge)
44protected:
45 KisCanvas2 *canvas() const override {
46 return m_canvas->canvas();
47 }
48 QOpenGLContext *openglContext() const override {
49 return m_canvas->context();
50 }
51 qreal devicePixelRatioF() const override {
52 return m_canvas->devicePixelRatioF();
53 }
57 QColor borderColor() const override {
58 return m_canvas->borderColor();
59 }
60 GLenum internalTextureFormat() const override {
61 return m_canvas->textureFormat();
62 }
63};
64
66{
67public:
69 delete renderer;
70 }
71
72 boost::optional<QRect> updateRect;
73#if KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
74 bool shouldSkipRenderingPass = false;
75#endif
78 QScopedPointer<KisOpenGLSync> glSyncObject;
80};
81
83 KisCoordinatesConverter *coordinatesConverter,
84 QWidget *parent,
85 KisImageWSP image,
86 const KisDisplayConfig &displayConfig,
88 BitDepthMode bitDepthRequest)
89 : QOpenGLWidget(parent)
90 , KisCanvasWidgetBase(canvas, coordinatesConverter)
91 , d(new Private())
92{
93 setProperty("krita_skip_srgb_surface_manager_assignment", true);
95
96 KisConfig cfg(false);
97 cfg.setCanvasState("OPENGL_STARTED");
98
99 d->renderer = new KisOpenGLCanvasRenderer(new CanvasBridge(this), image, displayConfig, displayFilter);
100
101 connect(d->renderer->openGLImageTextures().data(),
102 SIGNAL(sigShowFloatingMessage(QString, int, bool)),
103 SLOT(slotShowFloatingMessage(QString, int, bool)));
104
105 setAcceptDrops(true);
106 setAutoFillBackground(false);
107
108 setFocusPolicy(Qt::StrongFocus);
109 setAttribute(Qt::WA_NoSystemBackground, true);
110#ifdef Q_OS_MACOS
111 setAttribute(Qt::WA_AcceptTouchEvents, false);
112#else
113 setAttribute(Qt::WA_AcceptTouchEvents, true);
114#endif
115 setAttribute(Qt::WA_InputMethodEnabled, true);
116 setAttribute(Qt::WA_DontCreateNativeAncestors, true);
117
118 const bool osManagedSurfacePresent = KisPlatformPluginInterfaceFactory::instance()->surfaceColorManagedByOS();
119 const bool useNativeSurfaceForCanvas = cfg.effectiveShouldUseNativeSurfaceForCanvas(osManagedSurfacePresent);
120 if (useNativeSurfaceForCanvas) {
121 setAttribute(Qt::WA_NativeWindow, true);
122 }
123
124 setUpdateBehavior(PartialUpdate);
125
126 // we should make sure the texture doesn't have alpha channel,
127 // otherwise blending will not work correctly.
128 if (KisOpenGLModeProber::instance()->useHDRMode()) {
129 setTextureFormat(GL_RGBA16F);
130 } else {
131 if (bitDepthRequest == BitDepthMode::Depth10Bit) {
132 if (QSurfaceFormat::defaultFormat().redBufferSize() < 10) {
133 warnOpenGL <<
134 "WARNING: KisOpenGLCanvas2 was created with a 10-bit surface, "
135 "while the global surface format is still set to 8-bit. Expect "
136 "color banding to appear";
137 }
138 setTextureFormat(GL_RGB10_A2);
139 } else {
149 if (!KisOpenGL::hasOpenGLES()) {
150 setTextureFormat(GL_RGB8);
151 }
152 }
153 }
154
155 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
156 connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotPixelGridModeChanged()));
157
158 connect(canvas->viewManager()->canvasResourceProvider(), SIGNAL(sigEffectiveCompositeOpChanged()), SLOT(slotUpdateCursorColor()));
159 connect(canvas->viewManager()->canvasResourceProvider(), SIGNAL(sigPaintOpPresetChanged(KisPaintOpPresetSP)), SLOT(slotUpdateCursorColor()));
160
163 cfg.writeEntry("canvasState", "OPENGL_SUCCESS");
164}
165
167{
179 makeCurrent();
180
181 delete d;
182
183 doneCurrent();
184}
185
187{
188 KisOpenGLContextSwitchLockSkipOnQt5 contextLock(this);
189 d->renderer->setDisplayFilter(displayFilter);
190}
191
193{
194 KisOpenGLContextSwitchLockSkipOnQt5 contextLock(this);
196}
197
203
208
214
219
225
226void KisOpenGLCanvas2::resizeGL(int width, int height)
227{
228 d->renderer->resizeGL(width, height);
229 d->canvasImageDirtyRect = QRect(0, 0, width, height);
230}
231
233{
234#if KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
235 if (d->shouldSkipRenderingPass) {
236 return;
237 }
238#endif
239
240 const QRect updateRect = d->updateRect ? *d->updateRect : QRect();
241
242 if (!OPENGL_SUCCESS) {
243 KisConfig cfg(false);
244 cfg.writeEntry("canvasState", "OPENGL_PAINT_STARTED");
245 }
246
248 QRect canvasImageDirtyRect = d->canvasImageDirtyRect & rect();
249 d->canvasImageDirtyRect = QRect();
250 d->renderer->paintCanvasOnly(canvasImageDirtyRect, updateRect);
251 {
252 QPainter gc(this);
253 if (!updateRect.isEmpty()) {
254 gc.setClipRect(updateRect);
255 }
256
257 QRect decorationsBoundingRect = coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect();
258
259 if (!updateRect.isEmpty()) {
260 decorationsBoundingRect &= updateRect;
261 }
262
263 drawDecorations(gc, decorationsBoundingRect);
264 }
265
266 d->repaintDbg.paint(this, updateRect.isEmpty() ? rect() : updateRect);
267
268 // We create the glFenceSync object here instead of in KisOpenGLRenderer,
269 // because the glFenceSync object should be created after all render
270 // commands in a frame, not just the OpenGL canvas itself. Putting it
271 // outside of KisOpenGLRenderer allows the canvas widget to do extra
272 // rendering, which a QtQuick2-based canvas will need.
273 d->glSyncObject.reset(new KisOpenGLSync());
274
275 if (!OPENGL_SUCCESS) {
276 KisConfig cfg(false);
277 cfg.writeEntry("canvasState", "OPENGL_SUCCESS");
278 OPENGL_SUCCESS = true;
279 }
280}
281
283{
285
286 if (qFuzzyCompare(devicePixelRatioF(), qRound(devicePixelRatioF()))) {
294 d->updateRect = e->rect();
295 } else {
296 d->updateRect = this->rect();
297 }
298
299#if KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
305 if (isBusy()) {
306 //qWarning() << "WARNING: paint event delivered with the canvas non-ready, rescheduling...";
307 d->shouldSkipRenderingPass = true;
308 QOpenGLWidget::paintEvent(e);
309 d->shouldSkipRenderingPass = false;
310 QTimer::singleShot(0, this,
311 [this, updateRect = *d->updateRect] () {
312 if (updateRect.isEmpty()) {
313 this->update();
314 } else {
315 this->update(updateRect);
316 }
317 });
318 } else
319#endif
320 {
321 QOpenGLWidget::paintEvent(e);
322 }
323
324 d->updateRect = boost::none;
325}
326
328{
337 const QRect updateRect = d->updateRect ? *d->updateRect : QRect();
338
339 d->renderer->paintToolOutline(path, updateRect, thickness);
340}
341
343{
344 const bool isBusyStatus = d->glSyncObject && !d->glSyncObject->isSignaled();
346 return isBusyStatus;
347}
348
353
360
367
372
373void KisOpenGLCanvas2::slotShowFloatingMessage(const QString &message, int timeout, bool priority)
374{
375 canvas()->imageView()->showFloatingMessage(message, QIcon(), timeout, priority ? KisFloatingMessage::High : KisFloatingMessage::Medium);
376}
377
378QVariant KisOpenGLCanvas2::inputMethodQuery(Qt::InputMethodQuery query) const
379{
380 return processInputMethodQuery(query);
381}
382
383void KisOpenGLCanvas2::inputMethodEvent(QInputMethodEvent *event)
384{
386}
387
388void KisOpenGLCanvas2::focusInEvent(QFocusEvent *event)
389{
390 processFocusInEvent(event);
391}
392
393void KisOpenGLCanvas2::focusOutEvent(QFocusEvent *event)
394{
396}
397
399{
400 QOpenGLWidget::hideEvent(e);
402}
403
405{
406 QOpenGLWidget::showEvent(e);
408}
409
411{
412 KisOpenGLContextSwitchLockSkipOnQt5 contextLock(this);
413 d->renderer->setDisplayConfig(config);
414}
415
416void KisOpenGLCanvas2::channelSelectionChanged(const QBitArray &channelFlags)
417{
418 d->renderer->channelSelectionChanged(channelFlags);
419}
420
421
423{
424 KisOpenGLContextSwitchLockSkipOnQt5 contextLock(this);
426}
427
432
433
438
440{
441 KisOpenGLContextSwitchLockSkipOnQt5 contextLock(this);
443}
444
445void KisOpenGLCanvas2::updateCanvasImage(const QRect &imageUpdateRect)
446{
447 d->canvasImageDirtyRect |= imageUpdateRect;
448 update(imageUpdateRect);
449}
450
451void KisOpenGLCanvas2::updateCanvasDecorations(const QRect &decoUpdateRect)
452{
453 update(decoUpdateRect);
454}
456{
457 return focusNextPrevChild(next);
458}
459
464
466{
467 return
468 textureFormat() == GL_RGB10_A2 &&
469 format().redBufferSize() == 10 &&
470 format().greenBufferSize() == 10 &&
471 format().blueBufferSize() == 10 ?
474}
475
477 QString report;
478 QDebug str(&report);
479
480 str << "Texture Format: " << Qt::hex << Qt::showbase << textureFormat() << Qt::reset;
481
482 switch (textureFormat()) {
483 case GL_RGB10_A2:
484 str << " (" << "GL_RGB10_A2" << ")";
485 break;
486// not available in openGLES
487#ifdef GL_RGB10
488 case GL_RGB10:
489 str << " (" << "GL_RGB10" << ")";
490 break;
491#endif /* GL_RGB10 */
492// not available in openGLES
493#ifdef GL_RGB12
494 case GL_RGB12:
495 str << " (" << "GL_RGB12" << ")";
496 break;
497#endif /* GL_RGB12 */
498// not available in openGLES
499#ifdef GL_RGBA16
500 case GL_RGBA16:
501 str << " (" << "GL_RGBA16" << ")";
502 break;
503#endif /* GL_RGBA16 */
504// not available in openGLES
505#ifdef GL_RGB16
506 case GL_RGB16:
507 str << " (" << "GL_RGB16" << ")";
508 break;
509#endif /* GL_RGB16 */
510 case GL_RGBA16F:
511 str << " (" << "GL_RGBA16F" << ")";
512 break;
513 case GL_RGB8:
514 str << " (" << "GL_RGB8" << ")";
515 break;
516 case GL_RGBA8:
517 str << " (" << "GL_RGBA8" << ")";
518 break;
519 default:
520 str << " (" << "<unknown>" << ")";
521 break;
522 }
523 str << Qt::endl;
524
525 str << "FBO Buffer Size: "
526 << "R: " << format().redBufferSize() << " "
527 << "G: " << format().greenBufferSize() << " "
528 << "B: " << format().blueBufferSize() << " "
529 << "A: " << format().alphaBufferSize() << Qt::endl;
530
531 QWindow *win = windowHandle();
532 if (win) {
533 str << "Window Buffer Size: "
534 << "R: " << win->format().redBufferSize() << " "
535 << "G: " << win->format().greenBufferSize() << " "
536 << "B: " << win->format().blueBufferSize() << " "
537 << "A: " << win->format().alphaBufferSize() << Qt::endl;
538 }
539
540 if (win) {
541 str << "Global Buffer Size: "
542 << "R: " << QSurfaceFormat::defaultFormat().redBufferSize() << " "
543 << "G: " << QSurfaceFormat::defaultFormat().greenBufferSize() << " "
544 << "B: " << QSurfaceFormat::defaultFormat().blueBufferSize() << " "
545 << "A: " << QSurfaceFormat::defaultFormat().alphaBufferSize() << Qt::endl;
546 }
547
548 return report;
549}
float value(const T *src, size_t ch)
WrapAroundAxis
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:892
bool effectiveShouldUseNativeSurfaceForCanvas(bool osManagedSurfacePresent) const
KisDisplayConfig This class keeps track of the color management configuration for image to display....
static constexpr char ENABLED_PROPERTY[]
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:108
static bool OPENGL_SUCCESS
boost::optional< QRect > updateRect
QScopedPointer< KisOpenGLSync > glSyncObject
KisOpenGLCanvasRenderer * renderer