Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_canvas2.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 *
3 * SPDX-FileCopyrightText: 2006, 2010 Boudewijn Rempt <boud@valdyas.org>
4 * SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
5 * SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10#include "kis_canvas2.h"
11
12#include <functional>
13#include <numeric>
14
15#include <QApplication>
16#include <QWidget>
17#include <QVBoxLayout>
18#include <QTime>
19#include <QMouseEvent>
20#include <QScreen>
21#include <QScreen>
22#include <QWindow>
23
24#include <kis_debug.h>
25
26#include <KoUnit.h>
27#include <KoShapeManager.h>
29#include <KoColorProfile.h>
31#include <KisDocument.h>
32#include <KoSelection.h>
33#include <KoShapeController.h>
35#include <KoSvgTextShape.h>
36
37#include <KisUsageLogger.h>
38
39#include <kis_lod_transform.h>
40#include "kis_tool_proxy.h"
43#include "kis_image.h"
44#include "KisImageBarrierLock.h"
45#include "kis_undo_adapter.h"
48#include "KisViewManager.h"
49#include "kis_config.h"
50#include "kis_config_notifier.h"
52#include "kis_qpainter_canvas.h"
53#include "kis_group_layer.h"
55#include "kis_node_manager.h"
56#include "kis_selection.h"
59#include "kis_selection_mask.h"
60#include "kis_image_config.h"
66#include "KisView.h"
68#include "kis_grid_config.h"
69#include "KisMainWindow.h"
70
74#include "opengl/kis_opengl.h"
75#include "kis_fps_decoration.h"
76
79
81#include <kis_popup_palette.h>
82
85
87
90
91#include "kis_wrapped_rect.h"
92#include "kis_algebra_2d.h"
94
96#include "KisDisplayConfig.h"
97#include "config-qt-patches-present.h"
98#include <KoIcon.h>
99
100#include <config-use-surface-color-management-api.h>
101#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
102
106
107#endif /* KRITA_USE_SURFACE_COLOR_MANAGEMENT_API */
108
111#include <KisCanvasState.h>
112
113
114namespace {
115 struct ShapeLifetimeWrapper : KoShape::ShapeChangeListener
116 {
117 ShapeLifetimeWrapper(KoShape *shape, std::function<void()> destructionHandler)
118 : m_shape(shape),
119 m_destructionHandler(destructionHandler)
120 {
121 KIS_SAFE_ASSERT_RECOVER_RETURN(destructionHandler);
123
124 shape->addShapeChangeListener(this);
125 }
126
127 void notifyShapeChanged(KoShape::ChangeType type, KoShape *) override {
128 if (type == KoShape::Deleted) {
129 m_destructionHandler();
130 m_shape = nullptr;
131 }
132 }
133
134 KoShape* shape() const {
135 return m_shape;
136 }
137
138 private:
139 KoShape *m_shape = nullptr;
140 std::function<void()> m_destructionHandler;
141 };
142}
143
144class Q_DECL_HIDDEN KisCanvas2::KisCanvas2Private
145{
152 {
153 KisCanvas2Private * m_canvasPrivateRef = nullptr;
155 CanvasInputActionGroupsMaskInterface(KisCanvas2Private * canvasPrivateRef)
156 :m_canvasPrivateRef(canvasPrivateRef)
157 { }
158 KisInputActionGroupsMask inputActionGroupsMask() const override
159 {
160 Q_ASSERT(m_canvasPrivateRef); // this method should only be used upon creating a KisInputActionGroupsMaskGuard
161 return m_canvasPrivateRef->inputActionGroupsMask;
162 }
163 void setInputActionGroupsMask(KisInputActionGroupsMask mask) override
164 {
165 if(m_canvasPrivateRef)
166 m_canvasPrivateRef->inputActionGroupsMask = mask;
167 }
168 }; // class CanvasInputActionGroupsMask
169
170public:
172 KisCoordinatesConverter *coordConverter,
174 KoCanvasResourceProvider *resourceManager)
175 : q(parent)
176 , coordinatesConverter(coordConverter)
177 , view(view)
178 , shapeManager(parent)
179 , selectedShapesProxy(&shapeManager)
180 , toolProxy(parent)
181 , proofingConfig(new KisProofingConfiguration)
182 , displayColorConverter(resourceManager, view)
183 , inputActionGroupsMaskInterface(new CanvasInputActionGroupsMaskInterface(this))
184 , regionOfInterestUpdateCompressor(100, KisSignalCompressor::FIRST_INACTIVE)
185 , referencesBoundsUpdateCompressor(100, KisSignalCompressor::FIRST_INACTIVE)
186 {
187#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
188 if (KisPlatformPluginInterfaceFactory::instance()->surfaceColorManagedByOS()) {
189 rootSurfaceInfoProxy = new KisRootSurfaceInfoProxy(view, q);
190 multiSurfaceSetupManager.setRootSurfaceInfoProxy(rootSurfaceInfoProxy);
191 connect(rootSurfaceInfoProxy,
193 parent,
194 [this](const KoColorProfile *profile) {
195 if (!multiSurfaceState)
196 return;
197 auto newState = multiSurfaceSetupManager.onGuiSurfaceFormatChanged(*multiSurfaceState, profile);
198 assignChangedMultiSurfaceState(newState);
199 });
200 }
201#endif
202 }
203
205 {
206 inputActionGroupsMaskInterface->m_canvasPrivateRef = nullptr;
207
208 // We need to make sure that the QScopedPointer gets freed within the scope
209 // of KisCanvas2Private's lifespan. For some reason, this isn't guaranteed
210 // and was causing a crash when closing a file when playback is happening
211 // and there are multiple images. See Bug: 499658
212 animationPlayer.reset();
213 }
214
215
217 KisCoordinatesConverter *coordinatesConverter = 0;
219 KisAbstractCanvasWidget *canvasWidget = 0;
222 bool currentCanvasIsOpenGL = true;
223 std::optional<ShapeLifetimeWrapper> groupModeShapeWrapper;
224
225 int openGLFilterMode = 0;
228
229#if !KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
231#endif
234 bool updateSceneRequested = false;
235
236 QBitArray channelFlags;
238 bool proofingConfigUpdated = false;
239
240 KisPopupPalette *popupPalette = 0;
242
244 QScopedPointer<KisCanvasAnimationState> animationPlayer;
246 bool lodPreferredInImage = false;
247 bool bootstrapLodBlocked = false;
249 KisInputActionGroupsMask inputActionGroupsMask = AllActionGroup;
250
252
254
258 qreal regionOfInterestMargin = 0.25;
259
261 int isBatchUpdateActive = 0;
262
263
264#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
265 QScopedPointer<KisCanvasSurfaceColorSpaceManager> surfaceColorManager;
266 KisRootSurfaceInfoProxy *rootSurfaceInfoProxy;
267#endif
269 std::optional<KisMultiSurfaceStateManager::State> multiSurfaceState;
270
272 return lodPreferredInImage && !bootstrapLodBlocked;
273 }
274
275 bool lodIsSupported() const {
276 return currentCanvasIsOpenGL &&
278 (openGLFilterMode == KisOpenGL::TrilinearFilterMode ||
279 openGLFilterMode == KisOpenGL::HighQualityFiltering);
280 }
281
283
284 QRect docUpdateRectToWidget(const QRectF &docRect);
285
286 int currentScreenId() const {
287 int canvasScreenNumber = qApp->screens().indexOf(view->currentScreen());
288
289 if (canvasScreenNumber < 0) {
290 warnKrita << "Couldn't detect screen that Krita belongs to..." << ppVar(view->currentScreen());
291 canvasScreenNumber = 0;
292 }
293 return canvasScreenNumber;
294 }
295
298};
299
300namespace {
301KoShapeManager* fetchShapeManagerFromNode(KisNodeSP node)
302{
303 KoShapeManager *shapeManager = 0;
304 KisSelectionSP selection;
305
306 if (KisLayer *layer = dynamic_cast<KisLayer*>(node.data())) {
307 KisShapeLayer *shapeLayer = dynamic_cast<KisShapeLayer*>(layer);
308 if (shapeLayer) {
309 shapeManager = shapeLayer->shapeManager();
310
311 }
312 } else if (KisSelectionMask *mask = dynamic_cast<KisSelectionMask*>(node.data())) {
313 selection = mask->selection();
314 }
315
316 if (!shapeManager && selection && selection->hasShapeSelection()) {
317 KisShapeSelection *shapeSelection = dynamic_cast<KisShapeSelection*>(selection->shapeSelection());
318 KIS_ASSERT_RECOVER_RETURN_VALUE(shapeSelection, 0);
319
320 shapeManager = shapeSelection->shapeManager();
321 }
322
323 return shapeManager;
324}
325}
326
328 : KoCanvasBase(sc, resourceManager)
329 , m_d(new KisCanvas2Private(this, coordConverter, view, resourceManager))
330{
336 m_d->bootstrapLodBlocked = true;
337 connect(mainWindow, SIGNAL(guiLoadingFinished()), SLOT(bootstrapFinished()));
338
339 KisImageConfig config(false);
340
341#if !KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
342 m_d->canvasUpdateCompressor.setDelay(1000 / config.fpsLimit());
343 m_d->canvasUpdateCompressor.setMode(KisSignalCompressor::FIRST_ACTIVE);
344#endif
345
346 m_d->frameRenderStartCompressor.setDelay(1000 / config.fpsLimit());
347 m_d->frameRenderStartCompressor.setMode(KisSignalCompressor::FIRST_ACTIVE);
349}
350
352{
353 // a bit of duplication from slotConfigChanged()
354 KisConfig cfg(true);
355 m_d->lodPreferredInImage = cfg.levelOfDetailEnabled();
356 m_d->regionOfInterestMargin = KisImageConfig(true).animationCacheRegionOfInterestMargin();
357
358 createCanvas(cfg.useOpenGL());
359
360 setLodPreferredInCanvas(m_d->lodPreferredInImage);
361
362 connect(m_d->view->canvasController()->proxyObject, SIGNAL(effectiveZoomChanged(qreal)), SLOT(slotEffectiveZoomChanged(qreal)));
363 connect(m_d->view->canvasController()->proxyObject, &KoCanvasControllerProxyObject::canvasStateChanged, this, &KisCanvas2::slotCanvasStateChanged);
364
365 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
368
384 KisShapeController *kritaShapeController = static_cast<KisShapeController*>(shapeController()->documentBase());
385 connect(kritaShapeController, SIGNAL(selectionChanged()),
386 this, SLOT(slotSelectionChanged()));
387 connect(kritaShapeController, SIGNAL(selectionContentChanged()),
388 selectedShapesProxy(), SIGNAL(selectionContentChanged()));
389 connect(kritaShapeController, SIGNAL(currentLayerChanged(const KoShapeLayer*)),
390 selectedShapesProxy(), SIGNAL(currentLayerChanged(const KoShapeLayer*)));
391
392#if !KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
393 connect(&m_d->canvasUpdateCompressor, SIGNAL(timeout()), SLOT(slotDoCanvasUpdate()));
394#endif
395
396 connect(this, SIGNAL(sigCanvasCacheUpdated()), &m_d->frameRenderStartCompressor, SLOT(start()));
397 connect(&m_d->frameRenderStartCompressor, SIGNAL(timeout()), SLOT(updateCanvasProjection()));
398
399 connect(this, SIGNAL(sigContinueResizeImage(qint32,qint32)), SLOT(finishResizingImage(qint32,qint32)));
400
401 connect(&m_d->regionOfInterestUpdateCompressor, SIGNAL(timeout()), SLOT(slotUpdateRegionOfInterest()));
402 connect(&m_d->referencesBoundsUpdateCompressor, SIGNAL(timeout()), SLOT(slotUpdateReferencesBounds()));
403
404 connect(m_d->view->document(), SIGNAL(sigReferenceImagesChanged()), &m_d->referencesBoundsUpdateCompressor, SLOT(start()));
405
407
408 m_d->animationPlayer.reset(new KisCanvasAnimationState(this));
409}
410
412{
413 KisConfig cfg(true);
414
415 const bool shouldShowDebugOverlay =
418
419 if (shouldShowDebugOverlay && !decoration(KisFpsDecoration::idTag)) {
421
422 if (cfg.enableBrushSpeedLogging()) {
423 connect(KisStrokeSpeedMonitor::instance(), SIGNAL(sigStatsUpdated()), this, SLOT(updateCanvas()));
424 }
425 } else if (!shouldShowDebugOverlay && decoration(KisFpsDecoration::idTag)) {
426 m_d->canvasWidget->removeDecoration(KisFpsDecoration::idTag);
427 disconnect(KisStrokeSpeedMonitor::instance(), SIGNAL(sigStatsUpdated()), this, SLOT(updateCanvas()));
428 }
429}
430
432{
433 delete m_d;
434}
435
437{
438 if (m_d->popupPalette) {
439 m_d->popupPalette->setParent(widget->widget());
440 }
441
442 if (m_d->canvasWidget) {
448 widget->setDecorations(m_d->canvasWidget->decorations());
449
450 if(viewManager()) {
452 m_d->canvasWidget = widget;
454 } else {
455 m_d->canvasWidget = widget;
456 }
457 } else {
458 m_d->canvasWidget = widget;
459 }
460
461 if (!m_d->canvasWidget->decoration(INFINITY_DECORATION_ID)) {
462 KisInfinityManager *manager = new KisInfinityManager(m_d->view, this);
463 manager->setVisible(true);
464 m_d->canvasWidget->addDecoration(manager);
465 }
466
467 widget->widget()->setAutoFillBackground(false);
468 widget->widget()->setAttribute(Qt::WA_OpaquePaintEvent);
469 widget->widget()->setMouseTracking(true);
470 widget->widget()->setAcceptDrops(true);
471
473 if (controller && controller->canvas() == this) {
474 controller->changeCanvasWidget(widget->widget());
475 }
476
477
478#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
484 m_d->surfaceColorManager.reset();
485
486 QWindow *mainWindowNativeWindow = m_d->view->mainWindow()->windowHandle();
487 QWindow *nativeWindow = widget->widget()->windowHandle();
488
489 if (nativeWindow && nativeWindow != mainWindowNativeWindow) {
490 std::unique_ptr<KisSurfaceColorManagerInterface> iface(
491 KisPlatformPluginInterfaceFactory::instance()->createSurfaceColorManager(nativeWindow));
492
493 // if surfaceColorManagedByOS() is true, then interface is guaranteed to
494 // be present
496
497 if (iface) {
498 m_d->surfaceColorManager.reset(
499 new KisCanvasSurfaceColorSpaceManager(iface.release(),
500 m_d->multiSurfaceState->surfaceMode,
501 m_d->multiSurfaceState->multiConfig.options(),
502 this));
503
504 connect(m_d->surfaceColorManager.data(),
506 this,
508 }
509 } else {
510 qWarning() << "WARNING: created non-native Krita canvas on managed platform,"
511 << "its color space will be limited to sRGB";
512 }
513 }
514#endif
515}
516
518{
519 return m_d->currentCanvasIsOpenGL;
520}
521
523{
524 return KisOpenGL::FilterMode(m_d->openGLFilterMode);
525}
526
527void KisCanvas2::gridSize(QPointF *offset, QSizeF *spacing) const
528{
529 QTransform transform = coordinatesConverter()->imageToDocumentTransform();
530
531 const QPoint intSpacing = m_d->view->document()->gridConfig().spacing();
532 const QPoint intOffset = m_d->view->document()->gridConfig().offset();
533
534 QPointF size = transform.map(QPointF(intSpacing));
535 spacing->rwidth() = size.x();
536 spacing->rheight() = size.y();
537
538 *offset = transform.map(QPointF(intOffset));
539}
540
542{
543 return m_d->view->document()->gridConfig().snapToGrid();
544}
545
547{
548 return m_d->coordinatesConverter->rotationAngle();
549}
550
552{
553 return m_d->coordinatesConverter->xAxisMirrored();
554}
555
557{
558 return m_d->coordinatesConverter->yAxisMirrored();
559}
560
562{
563 KisImageSP image = this->image();
564 m_d->channelFlags = image->rootLayer()->channelFlags();
565
566 m_d->view->viewManager()->blockUntilOperationsFinishedForced(image);
567
569 m_d->canvasWidget->channelSelectionChanged(m_d->channelFlags);
571 image->unlock();
572}
573
575{
576 // This method exists to support flake-related operations
577 m_d->view->image()->undoAdapter()->addCommand(command);
578}
579
580void KisCanvas2::KisCanvas2Private::setActiveShapeManager(KoShapeManager *shapeManager)
581{
582 if (shapeManager != currentlyActiveShapeManager) {
583 currentlyActiveShapeManager = shapeManager;
584 selectedShapesProxy.setShapeManager(shapeManager);
585 }
586}
587
589{
591
592 // sanity check for consistency of the local shape manager
593 KIS_SAFE_ASSERT_RECOVER (localShapeManager == m_d->currentlyActiveShapeManager) {
595 }
596
598}
599
601{
602 return &m_d->selectedShapesProxy;
603}
604
606{
607 return &m_d->shapeManager;
608}
609
611{
612 return m_d->groupModeShapeWrapper ? m_d->groupModeShapeWrapper->shape() : nullptr;
613}
614
616{
617 if (currentShapeManagerOwnerShape() == source) return;
618
619 m_d->groupModeShapeWrapper = std::nullopt;
620
621 // we currently support entering text shapes only
622 if (source && dynamic_cast<KoSvgTextShape*>(source)) {
623 m_d->groupModeShapeWrapper.emplace(source, [this] () {
625 });
626 }
627
632}
633
635{
636 KisNodeSP node = m_d->view->currentNode();
637 KoShapeManager *localShapeManager = fetchShapeManagerFromNode(node);
638
644 !m_d->groupModeShapeWrapper ||
646 localShapeManager->shapes().contains(m_d->groupModeShapeWrapper->shape())) {
647
648 m_d->groupModeShapeWrapper = std::nullopt;
649 }
650
651 if (m_d->groupModeShapeWrapper) {
652 auto textShape = dynamic_cast<KoSvgTextShape*>(m_d->groupModeShapeWrapper->shape());
653
654 // we currently support entering text shapes only
655 KIS_SAFE_ASSERT_RECOVER(textShape) {
656 m_d->groupModeShapeWrapper = std::nullopt;
657 }
658
659 if (textShape) {
660 localShapeManager = textShape->internalShapeManager();
661 }
662 }
663
664 if (localShapeManager != m_d->currentlyActiveShapeManager) {
665 m_d->setActiveShapeManager(localShapeManager);
666 }
667
668 return localShapeManager;
669}
670
672{
673 return m_d->coordinatesConverter;
674}
675
677{
678 return m_d->coordinatesConverter;
679}
680
682{
683 return m_d->coordinatesConverter;
684}
685
687{
688 return m_d->displayColorConverter.displayRendererInterface();
689}
690
692{
693 return m_d->view->globalInputManager();
694}
695
697{
698 return m_d->canvasWidget->widget();
699}
700
701const QWidget* KisCanvas2::canvasWidget() const
702{
703 return m_d->canvasWidget->widget();
704}
705
706
708{
710
711 KisImageWSP image = m_d->view->image();
712 if (image) {
713 if (!qFuzzyCompare(image->xRes(), image->yRes())) {
714 warnKrita << "WARNING: resolution of the image is anisotropic"
715 << ppVar(image->xRes())
716 << ppVar(image->yRes());
717 }
718
719 const qreal resolution = image->xRes();
720 unit.setFactor(resolution);
721 }
722
723 return unit;
724}
725
727{
728 return &m_d->toolProxy;
729}
730
732{
733 m_d->currentCanvasIsOpenGL = false;
734
735 m_d->multiSurfaceState =
736 m_d->multiSurfaceSetupManager.createInitializingConfig(false, m_d->currentScreenId(), m_d->proofingConfig);
737
738 KisQPainterCanvas * canvasWidget = new KisQPainterCanvas(this, m_d->coordinatesConverter, m_d->view);
739 m_d->prescaledProjection = new KisPrescaledProjection();
740 m_d->prescaledProjection->setCoordinatesConverter(m_d->coordinatesConverter);
741 m_d->prescaledProjection->setDisplayConfig(m_d->multiSurfaceState->multiConfig.canvasDisplayConfig());
742 m_d->prescaledProjection->setDisplayFilter(m_d->displayColorConverter.displayFilter());
743 canvasWidget->setPrescaledProjection(m_d->prescaledProjection);
745}
746
748{
749 KisConfig cfg(true);
750 m_d->openGLFilterMode = cfg.openGLFilteringMode();
751 m_d->currentCanvasIsOpenGL = true;
752
753 m_d->multiSurfaceState =
754 m_d->multiSurfaceSetupManager.createInitializingConfig(true, m_d->currentScreenId(), m_d->proofingConfig);
755
756 auto bitDepthMode =
757 cfg.effectiveCanvasSurfaceBitDepthMode(QSurfaceFormat::defaultFormat())
761
763 m_d->coordinatesConverter,
764 0,
765 m_d->view->image(),
766 m_d->multiSurfaceState->multiConfig.canvasDisplayConfig(),
767 m_d->displayColorConverter.displayFilter(),
768 bitDepthMode);
769 m_d->frameCache = KisAnimationFrameCache::getFrameCache(canvasWidget->openGLImageTextures());
770
772}
773
774void KisCanvas2::createCanvas(bool useOpenGL)
775{
776 // deinitialize previous canvas structures
777 m_d->prescaledProjection = 0;
778 m_d->frameCache = 0;
779
780 KisConfig cfg(true);
781
782 if (useOpenGL && !KisOpenGL::hasOpenGL()) {
783 warnKrita << "Tried to create OpenGL widget when system doesn't have OpenGL\n";
784 useOpenGL = false;
785 }
786
787 if (useOpenGL) {
789 if (cfg.canvasState() == "OPENGL_FAILED") {
790 // Creating the opengl canvas failed, fall back
791 warnKrita << "OpenGL Canvas initialization returned OPENGL_FAILED. Falling back to QPainter.";
793 }
794 } else {
796 }
797
798 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->multiSurfaceState);
799 m_d->displayColorConverter.setMultiSurfaceDisplayConfig(m_d->multiSurfaceState->multiConfig);
800
801 if (m_d->popupPalette) {
802 m_d->popupPalette->setParent(m_d->canvasWidget->widget());
803 }
804
805}
806
808{
809 KisImageSP image = m_d->view->image();
810
811 m_d->displayColorConverter.setImageColorSpace(image->colorSpace());
812 m_d->coordinatesConverter->setImage(image);
813 m_d->toolProxy.initializeImage(image);
814
815 connect(image, SIGNAL(sigImageUpdated(QRect)), SLOT(startUpdateCanvasProjection(QRect)), Qt::DirectConnection);
816 connect(image->signalRouter(), SIGNAL(sigNotifyBatchUpdateStarted()), SLOT(slotBeginUpdatesBatch()), Qt::DirectConnection);
817 connect(image->signalRouter(), SIGNAL(sigNotifyBatchUpdateEnded()), SLOT(slotEndUpdatesBatch()), Qt::DirectConnection);
818 connect(image->signalRouter(), SIGNAL(sigRequestLodPlanesSyncBlocked(bool)), SLOT(slotSetLodUpdatesBlocked(bool)), Qt::DirectConnection);
819
820 connect(image, SIGNAL(sigProofingConfigChanged()), SLOT(slotChangeProofingConfig()));
821 connect(image, SIGNAL(sigSizeChanged(QPointF,QPointF)), SLOT(startResizingImage()), Qt::DirectConnection);
822 connect(image->undoAdapter(), SIGNAL(selectionChanged()), SLOT(slotTrySwitchShapeManager()));
823
824 connect(image, SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), SLOT(slotImageColorSpaceChanged()));
825 connect(image, SIGNAL(sigProfileChanged(const KoColorProfile*)), SLOT(slotImageColorSpaceChanged()));
826
829}
830
832{
833 KisImageSP image = m_d->view->image();
834
843 disconnect(image.data(), 0, this, 0);
844 image->unlock();
845}
846
848{
849 KisImageWSP image = m_d->view->image();
850
851 if (!m_d->currentCanvasIsOpenGL) {
852 Q_ASSERT(m_d->prescaledProjection);
853 m_d->prescaledProjection->setImage(image);
854 }
855
857 setLodPreferredInCanvas(m_d->lodPreferredInImage);
858
859 Q_EMIT sigCanvasEngineChanged();
860}
861
862void KisCanvas2::resetCanvas(bool useOpenGL)
863{
864 // we cannot reset the canvas before it's created, but this method might be called,
865 // for instance when setting the monitor profile.
866 if (!m_d->canvasWidget) {
867 return;
868 }
869
870 KisConfig cfg(true);
871
872 const bool canvasHasNativeSurface = bool(m_d->canvasWidget->widget()->windowHandle());
873 const bool canvasNeedsNativeSurface =
876
877 bool needReset = (m_d->currentCanvasIsOpenGL != useOpenGL) ||
878 (m_d->currentCanvasIsOpenGL &&
879 m_d->openGLFilterMode != cfg.openGLFilteringMode()) ||
880 canvasHasNativeSurface != canvasNeedsNativeSurface;
881
882 if (needReset) {
883 createCanvas(useOpenGL);
885 slotEffectiveZoomChanged(m_d->coordinatesConverter->effectiveZoom());
886 }
888}
889
890void KisCanvas2::startUpdateInPatches(const QRect &imageRect)
891{
897 if (m_d->currentCanvasIsOpenGL) {
899 } else {
900 KisImageConfig imageConfig(true);
901 int patchWidth = imageConfig.updatePatchWidth();
902 int patchHeight = imageConfig.updatePatchHeight();
903
904 for (int y = 0; y < imageRect.height(); y += patchHeight) {
905 for (int x = 0; x < imageRect.width(); x += patchWidth) {
906 QRect patchRect(x, y, patchWidth, patchHeight);
908 }
909 }
910 }
911}
912
914{
915 m_d->displayColorConverter.setDisplayFilter(displayFilter);
916 KisImageSP image = this->image();
917
918 m_d->view->viewManager()->blockUntilOperationsFinishedForced(image);
919
921 m_d->canvasWidget->setDisplayFilter(displayFilter);
922 image->unlock();
923}
924
926{
927 return m_d->displayColorConverter.displayFilter();
928}
929
931{
932 KisImageSP image = this->image();
933
934 m_d->view->viewManager()->blockUntilOperationsFinishedForced(image);
935
936 m_d->displayColorConverter.setImageColorSpace(image->colorSpace());
937 m_d->channelFlags = image->rootLayer()->channelFlags();
938 m_d->canvasWidget->channelSelectionChanged(m_d->channelFlags);
939
940 // Not all color spaces are supported by soft-proofing, so update state
941 if (imageView()->softProofing()) {
943 }
944
946 m_d->canvasWidget->notifyImageColorSpaceChanged(image->colorSpace());
947 image->unlock();
948}
949
951{
952 return &m_d->displayColorConverter;
953}
954
963
965{
967 if (!baseConfig) {
968 baseConfig = KisImageConfig(true).defaultProofingconfiguration();
969 }
970 *m_d->proofingConfig = *baseConfig;
971
973}
974
976{
977 KoColorConversionTransformation::ConversionFlags displayFlags = m_d->proofingConfig->displayFlags;
978 displayFlags.setFlag(KoColorConversionTransformation::SoftProofing, false);
979
980 if (image()->colorSpace()->colorDepthId().id().contains("U")) {
981 displayFlags.setFlag(KoColorConversionTransformation::SoftProofing, imageView()->softProofing());
982 displayFlags.setFlag(KoColorConversionTransformation::GamutCheck, imageView()->gamutCheck());
983 }
984 m_d->proofingConfig->displayFlags = displayFlags;
985 m_d->proofingConfigUpdated = true;
986
987 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->multiSurfaceState);
988 auto newState = m_d->multiSurfaceSetupManager.onProofingChanged(*m_d->multiSurfaceState, m_d->proofingConfig);
989 m_d->assignChangedMultiSurfaceState(newState);
990}
991
997
999{
1001 if (imageView()->softProofing()) {
1003 }
1004}
1005
1007{
1008 if (image() && !image()->proofingConfiguration()) {
1009 // global config should be updated only when
1010 // the image doesn't have its own config
1012 }
1013}
1014
1016{
1018 if (imageView()->softProofing()) {
1020 }
1021}
1022
1024{
1025 m_d->proofingConfigUpdated = updated;
1026}
1027
1029{
1030 return m_d->proofingConfigUpdated;
1031}
1032
1034{
1035 return m_d->proofingConfig;
1036}
1037
1039{
1040 KisImageWSP image = this->image();
1041 qint32 w = image->width();
1042 qint32 h = image->height();
1043
1044 Q_EMIT sigContinueResizeImage(w, h);
1045
1046 QRect imageBounds(0, 0, w, h);
1047 startUpdateInPatches(imageBounds);
1048}
1049
1050void KisCanvas2::finishResizingImage(qint32 w, qint32 h)
1051{
1052 m_d->canvasWidget->finishResizingImage(w, h);
1053}
1054
1056{
1057 KisUpdateInfoSP info = m_d->canvasWidget->startUpdateCanvasProjection(rc);
1058 if (m_d->projectionUpdatesCompressor.putUpdateInfo(info)) {
1059 Q_EMIT sigCanvasCacheUpdated();
1060 }
1061}
1062
1064{
1065 auto tryIssueCanvasUpdates = [this](const QRect &vRect) {
1066 if (!m_d->isBatchUpdateActive) {
1067 // TODO: Implement info->dirtyViewportRect() for KisOpenGLCanvas2 to avoid updating whole canvas
1068 if (m_d->currentCanvasIsOpenGL) {
1069 m_d->savedCanvasProjectionUpdateRect |= vRect;
1070
1071 // we already had a compression in frameRenderStartCompressor, so force the update directly
1073 } else if (/* !m_d->currentCanvasIsOpenGL && */ !vRect.isEmpty()) {
1074 m_d->savedCanvasProjectionUpdateRect |= m_d->coordinatesConverter->viewportToWidget(vRect).toAlignedRect();
1075
1076 // we already had a compression in frameRenderStartCompressor, so force the update directly
1078 }
1079 }
1080 };
1081
1082 auto uploadData = [this, tryIssueCanvasUpdates](const QVector<KisUpdateInfoSP> &infoObjects) {
1083 QVector<QRect> viewportRects = m_d->canvasWidget->updateCanvasProjection(infoObjects);
1084 const QRect vRect = std::accumulate(viewportRects.constBegin(), viewportRects.constEnd(),
1085 QRect(), std::bit_or<QRect>());
1086
1087 tryIssueCanvasUpdates(vRect);
1088 };
1089
1090 bool shouldExplicitlyIssueUpdates = false;
1091
1092 QVector<KisUpdateInfoSP> infoObjects;
1093 KisUpdateInfoList originalInfoObjects;
1094 m_d->projectionUpdatesCompressor.takeUpdateInfo(originalInfoObjects);
1095
1096 for (auto it = originalInfoObjects.constBegin();
1097 it != originalInfoObjects.constEnd();
1098 ++it) {
1099
1100 KisUpdateInfoSP info = *it;
1101
1102 const KisMarkerUpdateInfo *batchInfo = dynamic_cast<const KisMarkerUpdateInfo*>(info.data());
1103 if (batchInfo) {
1104 if (!infoObjects.isEmpty()) {
1105 uploadData(infoObjects);
1106 infoObjects.clear();
1107 }
1108
1109 if (batchInfo->type() == KisMarkerUpdateInfo::StartBatch) {
1110 m_d->isBatchUpdateActive++;
1111 } else if (batchInfo->type() == KisMarkerUpdateInfo::EndBatch) {
1112 m_d->isBatchUpdateActive--;
1113 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->isBatchUpdateActive >= 0);
1114 if (m_d->isBatchUpdateActive == 0) {
1115 shouldExplicitlyIssueUpdates = true;
1116 }
1117 } else if (batchInfo->type() == KisMarkerUpdateInfo::BlockLodUpdates) {
1118 m_d->canvasWidget->setLodResetInProgress(true);
1119 } else if (batchInfo->type() == KisMarkerUpdateInfo::UnblockLodUpdates) {
1120 m_d->canvasWidget->setLodResetInProgress(false);
1121 shouldExplicitlyIssueUpdates = true;
1122 }
1123 } else {
1124 infoObjects << info;
1125 }
1126 }
1127
1128 if (!infoObjects.isEmpty()) {
1129 uploadData(infoObjects);
1130 } else if (shouldExplicitlyIssueUpdates) {
1131 tryIssueCanvasUpdates(m_d->coordinatesConverter->imageRectInImagePixels());
1132 }
1133}
1134
1136{
1137 KisUpdateInfoSP info =
1139 m_d->coordinatesConverter->imageRectInImagePixels());
1140 m_d->projectionUpdatesCompressor.putUpdateInfo(info);
1141 Q_EMIT sigCanvasCacheUpdated();
1142}
1143
1145{
1146 KisUpdateInfoSP info =
1148 m_d->coordinatesConverter->imageRectInImagePixels());
1149 m_d->projectionUpdatesCompressor.putUpdateInfo(info);
1150 Q_EMIT sigCanvasCacheUpdated();
1151}
1152
1154{
1155 KisUpdateInfoSP info =
1159 m_d->coordinatesConverter->imageRectInImagePixels());
1160 m_d->projectionUpdatesCompressor.putUpdateInfo(info);
1161 Q_EMIT sigCanvasCacheUpdated();
1162}
1163
1165{
1170#if !KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
1171 m_d->canvasUpdateCompressor.start();
1172#else
1174#endif
1175}
1176
1178{
1179
1180#if !KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
1186 if (m_d->canvasWidget->isBusy()) {
1187 // just restarting the timer
1188 m_d->canvasUpdateCompressor.start();
1189 return;
1190 }
1191#endif
1192
1193 QRect combinedUpdateRect = m_d->savedCanvasProjectionUpdateRect | m_d->savedOverlayUpdateRect;
1194 if (!combinedUpdateRect.isEmpty()) {
1195 // TODO: Remove this signal (only used by the old KisSketchView)
1196 Q_EMIT updateCanvasRequested(combinedUpdateRect);
1197
1198 if (wrapAroundViewingMode() && !m_d->savedCanvasProjectionUpdateRect.isEmpty()) {
1199 const QRect widgetRect = m_d->canvasWidget->widget()->rect();
1200 const QRect imageRect = m_d->coordinatesConverter->imageRectInImagePixels();
1201
1202 const QRect widgetRectInImagePixels =
1203 m_d->coordinatesConverter->widgetToImage(widgetRect).toAlignedRect();
1204
1205 const QRect rc = m_d->coordinatesConverter->widgetToImage(m_d->savedCanvasProjectionUpdateRect).toAlignedRect();
1206
1207 const QVector<QRect> updateRects =
1208 KisWrappedRect::multiplyWrappedRect(rc, imageRect, widgetRectInImagePixels, wrapAroundViewingModeAxis());
1209
1210 Q_FOREACH(const QRect &rc, updateRects) {
1211 const QRect widgetUpdateRect =
1212 m_d->coordinatesConverter->imageToWidget(rc).toAlignedRect() & widgetRect;
1213 m_d->canvasWidget->updateCanvasImage(widgetUpdateRect);
1214 }
1215 m_d->canvasWidget->updateCanvasDecorations(m_d->savedOverlayUpdateRect);
1216 } else {
1217 m_d->canvasWidget->updateCanvasImage(m_d->savedCanvasProjectionUpdateRect);
1218 m_d->canvasWidget->updateCanvasDecorations(m_d->savedOverlayUpdateRect);
1219 }
1220 } else if (m_d->updateSceneRequested) {
1221 m_d->canvasWidget->widget()->update();
1222 }
1223
1224 m_d->savedCanvasProjectionUpdateRect = QRect();
1225 m_d->savedOverlayUpdateRect = QRect();
1226 m_d->updateSceneRequested = false;
1227}
1228
1230{
1231 QRect rect = m_d->canvasWidget->widget()->rect();
1232 if (!rc.isEmpty()) {
1233 rect &= rc;
1234 if (rect.isEmpty()) {
1235 return;
1236 }
1237 }
1238 // We don't know if it's the canvas projection or the overlay that's
1239 // changed, so we update both.
1240 m_d->savedCanvasProjectionUpdateRect |= rect;
1241 m_d->savedOverlayUpdateRect |= rect;
1243}
1244
1249
1250QRect KisCanvas2::KisCanvas2Private::docUpdateRectToWidget(const QRectF &docRect)
1251{
1252 QRect widgetRect = coordinatesConverter->documentToWidget(docRect).toAlignedRect();
1253 widgetRect.adjust(-2, -2, 2, 2);
1254 return widgetRect & canvasWidget->widget()->rect();
1255}
1256
1257void KisCanvas2::updateCanvas(const QRectF& documentRect)
1258{
1259 // updateCanvas is called from tools, never from the projection
1260 // updates, so no need to prescale!
1261 QRect widgetRect = m_d->docUpdateRectToWidget(documentRect);
1262 if (!widgetRect.isEmpty()) {
1263 updateCanvasWidgetImpl(widgetRect);
1264 }
1265}
1266
1267void KisCanvas2::updateCanvasProjection(const QRectF &docRect)
1268{
1269 QRect widgetRect = m_d->docUpdateRectToWidget(docRect);
1270 if (!widgetRect.isEmpty()) {
1271 m_d->savedCanvasProjectionUpdateRect |= widgetRect;
1273 }
1274}
1275
1277{
1278 m_d->savedOverlayUpdateRect = m_d->canvasWidget->widget()->rect();
1280}
1281
1282void KisCanvas2::updateCanvasDecorations(const QRectF &docRect)
1283{
1284 QRect widgetRect = m_d->docUpdateRectToWidget(docRect);
1285 if (!widgetRect.isEmpty()) {
1286 m_d->savedOverlayUpdateRect |= widgetRect;
1288 }
1289}
1290
1292{
1293 QRect widgetRect = m_d->docUpdateRectToWidget(docRect);
1294 if (!widgetRect.isEmpty()) {
1295 updateCanvasToolOutlineWdg(widgetRect);
1296 }
1297}
1298
1299void KisCanvas2::updateCanvasToolOutlineWdg(const QRect &widgetRect)
1300{
1301 QRect rect = widgetRect & m_d->canvasWidget->widget()->rect();
1302 if (!rect.isEmpty()) {
1303 m_d->savedOverlayUpdateRect |= rect;
1304#ifdef HAVE_NO_QT_UPDATE_COMPRESSIO
1305 m_d->canvasUpdateCompressor.start();
1306#else
1308#endif
1309 }
1310}
1311
1313{
1314 m_d->updateSceneRequested = true;
1316}
1317
1319{
1321 m_d->view->disconnect(object);
1322}
1323
1325{
1326 Q_UNUSED(newZoom)
1327
1328
1329
1331}
1332
1333QRect KisCanvas2::regionOfInterest() const
1334{
1335 return m_d->regionOfInterest;
1336}
1337
1339{
1340 const QRect oldRegionOfInterest = m_d->regionOfInterest;
1341
1342 const qreal ratio = m_d->regionOfInterestMargin;
1343 const QRect proposedRoi = KisAlgebra2D::blowRect(m_d->coordinatesConverter->widgetRectInImagePixels(), ratio).toAlignedRect();
1344
1345 const QRect imageRect = m_d->coordinatesConverter->imageRectInImagePixels();
1346
1347 m_d->regionOfInterest = proposedRoi & imageRect;
1348
1349 if (m_d->regionOfInterest != oldRegionOfInterest) {
1350 Q_EMIT sigRegionOfInterestChanged(m_d->regionOfInterest);
1351 }
1352}
1353
1355{
1356 QRectF referencesRect;
1357 KisReferenceImagesLayerSP layer = m_d->view->document()->referenceImagesLayer();
1358 if (layer) {
1359 referencesRect = layer->boundingImageRect();
1360 }
1361
1362 m_d->view->canvasController()->syncOnReferencesChange(referencesRect);
1363}
1364
1366{
1367 m_d->renderingLimit = rc;
1368}
1369
1370QRect KisCanvas2::renderingLimit() const
1371{
1372 return m_d->renderingLimit;
1373}
1374
1376{
1377 return m_d->popupPalette;
1378}
1379
1381{
1382 // Still needs to be recalculated in some cases, like when deleting vector layers
1384}
1385
1387{
1388 KisImageSP image = this->image();
1389
1390 if (m_d->bootstrapLodBlocked || !m_d->lodIsSupported()) {
1392 } else {
1393 const qreal effectiveZoom = m_d->coordinatesConverter->effectiveZoom();
1394
1395 KisConfig cfg(true);
1396 const int maxLod = cfg.numMipmapLevels();
1397 const int lod = KisLodTransform::scaleToLod(effectiveZoom, maxLod);
1398 KisLodPreferences::PreferenceFlags flags = KisLodPreferences::LodSupported;
1399
1400 if (m_d->lodPreferredInImage) {
1402 }
1404 }
1405}
1406
1408{
1409 if (m_d->view) {
1410 return m_d->view->viewManager();
1411 }
1412 return 0;
1413}
1414
1416{
1417 return m_d->view;
1418}
1419
1421{
1422 return m_d->view->image();
1423
1424}
1425
1427{
1428 return m_d->view->image();
1429}
1430
1432{
1433 if (!m_d->currentCanvasIsOpenGL) {
1434 Q_ASSERT(m_d->prescaledProjection);
1435 auto state = KisCanvasState::fromConverter(*m_d->coordinatesConverter);
1436 m_d->prescaledProjection->notifyCanvasStateChanged(state);
1437 }
1438
1439 updateCanvas();
1440 m_d->regionOfInterestUpdateCompressor.start();
1441
1442 Q_EMIT sigCanvasStateChanged();
1443}
1444
1446{
1447 KisConfig cfg(true);
1448 m_d->regionOfInterestMargin = KisImageConfig(true).animationCacheRegionOfInterestMargin();
1449
1450 resetCanvas(cfg.useOpenGL());
1451
1452 QWidget *mainWindow = m_d->view->mainWindow();
1454
1455 QWidget *topLevelWidget = mainWindow->topLevelWidget();
1456 KIS_SAFE_ASSERT_RECOVER_RETURN(topLevelWidget);
1457
1458 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->multiSurfaceState);
1459 auto newState = m_d->multiSurfaceSetupManager.onConfigChanged(*m_d->multiSurfaceState,
1460 m_d->currentScreenId(),
1463 m_d->assignChangedMultiSurfaceState(newState);
1464
1466}
1467
1469{
1470 const int screenId = qApp->screens().indexOf(screen);
1471
1472 if (screenId < 0) {
1473 warnUI << "Failed to get screenNumber for updating display profile.";
1474 return;
1475 }
1476
1477 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->multiSurfaceState);
1478 auto newState = m_d->multiSurfaceSetupManager.onScreenChanged(*m_d->multiSurfaceState,
1479 screenId);
1480 m_d->assignChangedMultiSurfaceState(newState);
1481}
1482
1484{
1485 KisImageSP image = this->image();
1486 KisImageReadOnlyBarrierLock l(image);
1488}
1489
1490void KisCanvas2::KisCanvas2Private::assignChangedMultiSurfaceStateSkipCanvasSurface(const KisMultiSurfaceStateManager::State &newState)
1491{
1492 // the surface state is supposed to be initialized on canvas creation
1493 KIS_SAFE_ASSERT_RECOVER_RETURN(multiSurfaceState);
1494
1495 if (*multiSurfaceState == newState) return;
1496
1497 const KisMultiSurfaceStateManager::State oldState = *this->multiSurfaceState;
1498 this->multiSurfaceState = newState;
1499
1500 displayColorConverter.setMultiSurfaceDisplayConfig(newState.multiConfig);
1501
1502 if (oldState.multiConfig.canvasDisplayConfig() != newState.multiConfig.canvasDisplayConfig()) {
1503 KisImageSP image = view->image();
1504 KisImageReadOnlyBarrierLock l(image);
1505 canvasWidget->setDisplayConfig(multiSurfaceState->multiConfig.canvasDisplayConfig());
1506
1507 q->refetchDataFromImage();
1508
1509 // we changed the canvas conversion mode, so the canvas background color
1510 // has changed as well
1511 q->updateCanvas();
1512 }
1513}
1514
1515void KisCanvas2::KisCanvas2Private::assignChangedMultiSurfaceState(const KisMultiSurfaceStateManager::State &newState)
1516{
1517 assignChangedMultiSurfaceStateSkipCanvasSurface(newState);
1518
1519#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
1520
1521 if (surfaceColorManager) {
1522 surfaceColorManager->setDisplayConfigOptions(newState.surfaceMode, newState.multiConfig.options());
1523 }
1524
1525#endif
1526}
1527
1529{
1530#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
1531
1532 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->multiSurfaceState);
1533 if (m_d->multiSurfaceState->multiConfig.canvasDisplayConfig() == config) return;
1534
1535 if (m_d->multiSurfaceState->isCanvasOpenGL) {
1536 if (config.isHDR &&
1537 m_d->canvasWidget->currentBitDepthMode() < KisOpenGLCanvas2::BitDepthMode::Depth10Bit) {
1538
1539 const QString warningMessage = i18n(
1540 "WARNING: HDR mode was activated on surface working in 8-bit mode!\n"
1541 "Please activate 10-bit mode in Krita's Preferences dialog and restart "
1542 "Krita to avoid color banding!");
1543
1544 m_d->view->showFloatingMessage(warningMessage, koIcon("warning"), 7000, KisFloatingMessage::High);
1545 warnOpenGL.noquote() << QString(warningMessage).replace('\n', ' ');
1546 warnOpenGL << ppVar(QSurfaceFormat::defaultFormat());
1547 }
1548 }
1549
1550 auto newState = m_d->multiSurfaceSetupManager.onCanvasSurfaceFormatChanged(*m_d->multiSurfaceState, config);
1551 m_d->assignChangedMultiSurfaceStateSkipCanvasSurface(newState);
1552#else
1553 Q_UNUSED(config);
1554#endif
1555}
1556
1558{
1559 m_d->canvasWidget->addDecoration(deco);
1560}
1561
1563{
1564 return m_d->canvasWidget->decoration(id);
1565}
1566
1567
1569{
1576 return QPoint();
1577}
1578
1580{
1581 return m_d->coordinatesConverter->documentOffset();
1582}
1583
1585{
1586 m_d->popupPalette = new KisPopupPalette(viewManager(), m_d->coordinatesConverter, favoriteResourceManager, displayColorConverter()->displayRendererInterface(),
1587 m_d->canvasWidget->widget());
1588 connect(m_d->popupPalette, SIGNAL(zoomLevelChanged(int)), this, SLOT(slotPopupPaletteRequestedZoomChange(int)));
1589 connect(m_d->popupPalette, SIGNAL(sigUpdateCanvas()), this, SLOT(updateCanvas()));
1590 connect(m_d->view->mainWindow(), SIGNAL(themeChanged()), m_d->popupPalette, SLOT(slotUpdateIcons()));
1591}
1592
1594 m_d->view->canvasController()->setZoom(KoZoomMode::ZOOM_CONSTANT, (qreal)(zoom/100.0)); // 1.0 is 100% zoom
1595}
1596
1597void KisCanvas2::setCursor(const QCursor &cursor)
1598{
1599 canvasWidget()->setCursor(cursor);
1600}
1601
1603{
1604 return m_d->frameCache;
1605}
1606
1608{
1609 return m_d->animationPlayer.data();
1610}
1611
1613{
1614 KisShapeLayer* shapeLayer = dynamic_cast<KisShapeLayer*>(viewManager()->activeLayer().data());
1615 if (!shapeLayer) {
1616 return;
1617 }
1618 m_d->shapeManager.selection()->deselectAll();
1619 Q_FOREACH (KoShape* shape, shapeLayer->shapeManager()->selection()->selectedShapes()) {
1620 m_d->shapeManager.selection()->select(shape);
1621 }
1622}
1623
1625{
1626 KisCanvasDecorationSP infinityDecoration =
1627 m_d->canvasWidget->decoration(INFINITY_DECORATION_ID);
1628
1629 if (infinityDecoration) {
1630 infinityDecoration->setVisible(!value);
1631 }
1632
1633 m_d->canvasWidget->setWrapAroundViewingMode(value);
1634}
1635
1637{
1638 return m_d->canvasWidget->wrapAroundViewingMode();
1639}
1640
1642{
1643 m_d->canvasWidget->setWrapAroundViewingModeAxis(value);
1644 updateCanvas();
1645}
1646
1648{
1649 return m_d->canvasWidget->wrapAroundViewingModeAxis();
1650}
1651
1653{
1654 if (!m_d->bootstrapLodBlocked) return;
1655
1656 m_d->bootstrapLodBlocked = false;
1657 setLodPreferredInCanvas(m_d->lodPreferredInImage);
1658
1659 // Initialization of audio tracks is deferred until after canvas has been completely constructed.
1660 m_d->animationPlayer->setupAudioTracks();
1661}
1662
1664{
1665 if (!KisOpenGL::supportsLoD()) {
1666 qWarning() << "WARNING: Level of Detail functionality is available only with openGL + GLSL 1.3 support";
1667 }
1668
1669 m_d->lodPreferredInImage =
1670 value && m_d->lodIsSupported();
1671
1673
1674 KisConfig cfg(false);
1675 cfg.setLevelOfDetailEnabled(m_d->lodPreferredInImage);
1676}
1677
1679{
1680 return m_d->lodPreferredInImage;
1681}
1682
1684{
1685 KisCanvasDecorationSP deco = decoration("paintingAssistantsDecoration");
1686 return qobject_cast<KisPaintingAssistantsDecoration*>(deco.data());
1687}
1688
1690{
1691 KisCanvasDecorationSP deco = decoration("referenceImagesDecoration");
1692 return qobject_cast<KisReferenceImagesDecoration*>(deco.data());
1693}
1694
1696{
1697 return m_d->inputActionGroupsMaskInterface;
1698}
1699
1701{
1702#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
1703 QString report;
1704 QDebug str(&report);
1705
1706 if (m_d->canvasWidget) {
1707 str << "(canvas bit depth report)" << Qt::endl;
1708 str << Qt::endl;
1709 str.noquote().nospace() << m_d->canvasWidget->currentBitDepthUserReport();
1710 }
1711
1712 str << Qt::endl;
1713
1714 if (m_d->surfaceColorManager) {
1715 str.noquote().nospace() << QString("(canvas surface color manager)\n");
1716 str.noquote().nospace() << QString("\n");
1717 str.noquote().nospace() << m_d->surfaceColorManager->colorManagementReport();
1718 } else {
1719 str.noquote().nospace() << QString("Surface color management is not supported on this platform\n");
1720 }
1721
1722 return report;
1723#else
1724 return "Surface color management is disabled\n";
1725#endif
1726}
float value(const T *src, size_t ch)
@ AllActionGroup
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
WrapAroundAxis
virtual void setDecorations(const QList< KisCanvasDecorationSP > &)=0
virtual QWidget * widget()=0
static KisAnimationFrameCacheSP getFrameCache(KisOpenGLImageTexturesSP textures)
KisReferenceImagesDecorationSP referenceImagesDecoration() const
qreal rotationAngle() const
canvas rotation in degrees
KisSelectedShapesProxy selectedShapesProxy
void startUpdateInPatches(const QRect &imageRect)
bool effectiveLodAllowedInImage() const
void slotScreenChanged(QScreen *screen)
void setLodPreferredInCanvas(bool value)
KisPopupPalette * popupPalette
KisCanvas2Private *const m_d
KisCanvasAnimationState * animationState() const
void refetchDataFromImage()
QRect regionOfInterest
void slotGamutCheck()
void slotBeginUpdatesBatch()
QScopedPointer< KisCanvasAnimationState > animationPlayer
void updateCanvasToolOutlineDoc(const QRectF &docRect)
void initializeFpsDecoration()
bool proofingConfigUpdated
void assignChangedMultiSurfaceStateSkipCanvasSurface(const KisMultiSurfaceStateManager::State &newState)
void setFavoriteResourceManager(KisFavoriteResourceManager *favoriteResourceManager)
KoUnit unit() const override
KisExposureGammaCorrectionInterface * exposureGammaCorrectionInterface() const
QRect renderingLimit
void slotCanvasStateChanged()
void fetchProofingOptions()
fetchProofingOptions Get the options for softproofing, and apply the view-specific state without affe...
QPointer< KisView > view
void sigCanvasStateChanged()
KoColorDisplayRendererInterface * displayRendererInterface() const override
displayRendererInterface The display renderer interface has a number of color conversion functions wh...
KisDisplayColorConverter displayColorConverter
void sigContinueResizeImage(qint32 w, qint32 h)
void startResizingImage()
void setCursor(const QCursor &cursor) override
void gridSize(QPointF *offset, QSizeF *spacing) const override
bool xAxisMirrored() const
Bools indicating canvasmirroring.
void slotUpdateReferencesBounds()
void setDisplayFilter(QSharedPointer< KisDisplayFilter > displayFilter)
bool wrapAroundViewingMode() const
KisInputManager * globalInputManager() const
void slotEndUpdatesBatch()
KisAnimationFrameCacheSP frameCache
KisImageWSP currentImage() const
void slotSelectionChanged()
void setWrapAroundViewingMode(bool value)
QRect savedCanvasProjectionUpdateRect
void createQPainterCanvas()
std::optional< KisMultiSurfaceStateManager::State > multiSurfaceState
void resetCanvas(bool useOpenGL)
KisProofingConfigurationSP proofingConfig
KisCoordinatesConverter * coordinatesConverter
void bootstrapFinished()
void setCurrentShapeManagerOwnerShape(KoShape *source) override
sets the group shape that is supposed to be "entered"
void setProofingConfigUpdated(bool updated)
setProofingConfigUpdated This function is to set whether the proofing config is updated,...
QRect savedOverlayUpdateRect
KisImageWSP image() const
void addCommand(KUndo2Command *command) override
void updateProofingState()
KoShape * currentShapeManagerOwnerShape() const override
the shape that owns the currently active shape manager
KoShapeManager * localShapeManager() const
void initializeImage()
WrapAroundAxis wrapAroundViewingModeAxis() const
void addDecoration(KisCanvasDecorationSP deco)
KoShapeManager shapeManager
KisViewManager * viewManager() const
void slotSurfaceFormatChanged(const KisDisplayConfig &config)
QSharedPointer< CanvasInputActionGroupsMaskInterface > inputActionGroupsMaskInterface
bool canvasIsOpenGL() const override
void slotPopupPaletteRequestedZoomChange(int zoom)
~KisCanvas2() override
int currentScreenId() const
QPointer< KisView > imageView() const
void setRenderingLimit(const QRect &rc)
void createCanvas(bool useOpenGL)
QRect docUpdateRectToWidget(const QRectF &docRect)
QPointer< KoShapeManager > currentlyActiveShapeManager
void slotConfigChanged()
KisAbstractCanvasWidget * canvasWidget
void assignChangedMultiSurfaceState(const KisMultiSurfaceStateManager::State &newState)
KisCanvas2(KisCoordinatesConverter *coordConverter, KoCanvasResourceProvider *resourceManager, KisMainWindow *mainWindow, KisView *view, KoShapeControllerBase *sc)
bool lodPreferredInCanvas() const
void updateCanvasScene()
KisToolProxy toolProxy
void disconnectImage()
void requestCanvasUpdateMaybeCompressed()
void slotSoftProofing()
KoShapeManager * globalShapeManager() const
void updateCanvasProjection()
KisSignalCompressor referencesBoundsUpdateCompressor
KisCanvasDecorationSP decoration(const QString &id) const
KisCanvasUpdatesCompressor projectionUpdatesCompressor
QPoint documentOrigin() const override
void slotSetLodUpdatesBlocked(bool value)
void startUpdateCanvasProjection(const QRect &rc)
QPoint documentOffset() const
KisSignalCompressor canvasUpdateCompressor
void setCanvasWidget(KisAbstractCanvasWidget *widget)
KisPaintingAssistantsDecorationSP paintingAssistantsDecoration() const
void slotImageColorSpaceChanged()
void disconnectCanvasObserver(QObject *object) override
void updateCanvasDecorations()
void notifyLevelOfDetailChange()
void slotChangeGlobalProofingConfig()
void finishResizingImage(qint32 w, qint32 h)
bool yAxisMirrored() const
void slotChangeProofingConfig()
void slotDoCanvasUpdate()
std::optional< ShapeLifetimeWrapper > groupModeShapeWrapper
KisProofingConfigurationSP proofingConfiguration() const
bool lodIsSupported() const
void connectCurrentCanvas()
void slotUpdateRegionOfInterest()
void updateCanvas()
Update the entire canvas area.
void setActiveShapeManager(KoShapeManager *shapeManager)
void updateCanvasWidgetImpl(const QRect &rc=QRect())
KisCanvas2Private(KisCanvas2 *parent, KisCoordinatesConverter *coordConverter, QPointer< KisView > view, KoCanvasResourceProvider *resourceManager)
void updateCanvasToolOutlineWdg(const QRect &widgetRect)
void createOpenGLCanvas()
void channelSelectionChanged()
KisMultiSurfaceStateManager multiSurfaceSetupManager
QString colorManagementReport() const
void sigCanvasEngineChanged()
void slotEffectiveZoomChanged(qreal newZoom)
void slotTrySwitchShapeManager()
KisSignalCompressor regionOfInterestUpdateCompressor
KisPrescaledProjectionSP prescaledProjection
const KoViewConverter * viewConverter() const override
void sigRegionOfInterestChanged(const QRect &roi)
QSharedPointer< KisDisplayFilter > displayFilter() const
QBitArray channelFlags
bool snapToGrid() const override
void sigCanvasCacheUpdated()
KisSignalCompressor frameRenderStartCompressor
void updateCanvasRequested(const QRect &rc)
void setWrapAroundViewingModeAxis(WrapAroundAxis value)
The KisCanvasAnimationState class stores all of the canvas-specific animation state.
virtual void setVisible(bool v)
static KisCanvasState fromConverter(const KisCoordinatesConverter &converter)
static KisConfigNotifier * instance()
int openGLFilteringMode(bool defaultValue=false) const
CanvasSurfaceBitDepthMode effectiveCanvasSurfaceBitDepthMode(const QSurfaceFormat &format) const
int numMipmapLevels(bool defaultValue=false) const
bool levelOfDetailEnabled(bool defaultValue=false) const
bool enableOpenGLFramerateLogging(bool defaultValue=false) const
CanvasSurfaceMode canvasSurfaceColorSpaceManagementMode(bool defaultValue=false) const
void setLevelOfDetailEnabled(bool value)
bool enableCanvasSurfaceColorSpaceManagement(bool defaultValue=false) const
bool enableBrushSpeedLogging(bool defaultValue=false) const
QString canvasState(bool defaultValue=false) const
bool useOpenGL(bool defaultValue=false) const
KisDisplayConfig This class keeps track of the color management configuration for image to display....
static Options optionsFromKisConfig(const KisConfig &cfg)
static const QString idTag
static KisImageConfigNotifier * instance()
void globalProofingConfigChanged()
int updatePatchWidth() const
KisProofingConfigurationSP defaultProofingconfiguration(bool requestDefault=false)
int updatePatchHeight() const
int fpsLimit(bool defaultValue=false) const
qreal animationCacheRegionOfInterestMargin(bool defaultValue=false) const
KisUndoAdapter * undoAdapter() const
void setLodPreferences(const KisLodPreferences &value)
KisGroupLayerSP rootLayer() const
const KoColorSpace * colorSpace() const
void unlock()
Definition kis_image.cc:806
void barrierLock(bool readOnly=false)
Wait until all the queued background jobs are completed and lock the image.
Definition kis_image.cc:757
qint32 width() const
void immediateLockForReadOnly()
Definition kis_image.cc:794
double xRes() const
double yRes() const
qint32 height() const
KisImageSignalRouter * signalRouter()
QRect bounds() const override
KisProofingConfigurationSP proofingConfiguration() const
proofingConfiguration
Central object to manage canvas input.
void removeTrackedCanvas(KisCanvas2 *canvas)
void addTrackedCanvas(KisCanvas2 *canvas)
static int scaleToLod(qreal scale, int maxLod)
Main window for Krita.
KisDisplayConfig canvasDisplayConfig() const
static bool hasOpenGL()
Check for OpenGL.
static bool supportsLoD()
@ HighQualityFiltering
Definition kis_opengl.h:37
@ TrilinearFilterMode
Definition kis_opengl.h:36
static KisPlatformPluginInterfaceFactory * instance()
The KisProofingConfiguration struct Little struct that stores the proofing configuration for a given ...
void sigRootSurfaceProfileChanged(const KoColorProfile *profile) const
KoShapeManager * shapeManager() const
KoShapeManager * shapeManager() const
static KisStrokeSpeedMonitor * instance()
KisLayerSP activeLayer()
Convenience method to get at the active layer.
KisInputManager * inputManager() const
Filters events and sends them to canvas actions.
KoSnapGuide * snapGuide
virtual void disconnectCanvasObserver(QObject *object)
QPointer< KoShapeController > shapeController
KoCanvasController * controller
KoCanvasController * canvasController() const
virtual KoCanvasBase * canvas() const
The KoSelectedShapesProxy class is a special interface of KoCanvasBase to have a stable connection to...
const QList< KoShape * > selectedShapes() const
QList< KoShape * > shapes
KoSelection * selection
void addShapeChangeListener(ShapeChangeListener *listener)
Definition KoShape.cpp:1152
ChangeType
Used by shapeChanged() to select which change was made.
Definition KoShape.h:92
@ Deleted
the shape was deleted
Definition KoShape.h:101
void overrideSnapStrategy(Strategy type, KoSnapStrategy *strategy)
@ Pixel
Definition KoUnit.h:82
void setFactor(qreal factor)
Definition KoUnit.h:122
@ ZOOM_CONSTANT
zoom x %
Definition KoZoomMode.h:24
static bool qFuzzyCompare(half p1, half p2)
#define KIS_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:85
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define warnKrita
Definition kis_debug.h:87
#define ppVar(var)
Definition kis_debug.h:155
#define warnOpenGL
Definition kis_debug.h:102
#define warnUI
Definition kis_debug.h:94
#define koIcon(name)
Use these macros for icons without any issues.
Definition kis_icon.h:25
static const QString INFINITY_DECORATION_ID
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
Rect blowRect(const Rect &rect, qreal coeff)
CanvasInputActionGroupsMaskInterface(KisCanvas2Private *canvasPrivateRef)
KisInputActionGroupsMask inputActionGroupsMask() const override
void setInputActionGroupsMask(KisInputActionGroupsMask mask) override
void sigDisplayConfigChanged(const KisDisplayConfig &config)
static KisDumbExposureGammaCorrectionInterface * instance()
QBitArray channelFlags
Definition kis_layer.cc:167
KisSelectionComponent * shapeSelection
bool hasShapeSelection() const
static QVector< QRect > multiplyWrappedRect(const QRect &rc, const QRect &wrapRect, const QRect &limitRect, WrapAroundAxis wrapAxis)