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->view->globalInputManager();
689}
690
692{
693 return m_d->canvasWidget->widget();
694}
695
696const QWidget* KisCanvas2::canvasWidget() const
697{
698 return m_d->canvasWidget->widget();
699}
700
701
703{
705
706 KisImageWSP image = m_d->view->image();
707 if (image) {
708 if (!qFuzzyCompare(image->xRes(), image->yRes())) {
709 warnKrita << "WARNING: resolution of the image is anisotropic"
710 << ppVar(image->xRes())
711 << ppVar(image->yRes());
712 }
713
714 const qreal resolution = image->xRes();
715 unit.setFactor(resolution);
716 }
717
718 return unit;
719}
720
722{
723 return &m_d->toolProxy;
724}
725
727{
728 m_d->currentCanvasIsOpenGL = false;
729
730 m_d->multiSurfaceState =
731 m_d->multiSurfaceSetupManager.createInitializingConfig(false, m_d->currentScreenId(), m_d->proofingConfig);
732
733 KisQPainterCanvas * canvasWidget = new KisQPainterCanvas(this, m_d->coordinatesConverter, m_d->view);
734 m_d->prescaledProjection = new KisPrescaledProjection();
735 m_d->prescaledProjection->setCoordinatesConverter(m_d->coordinatesConverter);
736 m_d->prescaledProjection->setDisplayConfig(m_d->multiSurfaceState->multiConfig.canvasDisplayConfig());
737 m_d->prescaledProjection->setDisplayFilter(m_d->displayColorConverter.displayFilter());
738 canvasWidget->setPrescaledProjection(m_d->prescaledProjection);
740}
741
743{
744 KisConfig cfg(true);
745 m_d->openGLFilterMode = cfg.openGLFilteringMode();
746 m_d->currentCanvasIsOpenGL = true;
747
748 m_d->multiSurfaceState =
749 m_d->multiSurfaceSetupManager.createInitializingConfig(true, m_d->currentScreenId(), m_d->proofingConfig);
750
751 auto bitDepthMode =
752 cfg.effectiveCanvasSurfaceBitDepthMode(QSurfaceFormat::defaultFormat())
756
758 m_d->coordinatesConverter,
759 0,
760 m_d->view->image(),
761 m_d->multiSurfaceState->multiConfig.canvasDisplayConfig(),
762 m_d->displayColorConverter.displayFilter(),
763 bitDepthMode);
764 m_d->frameCache = KisAnimationFrameCache::getFrameCache(canvasWidget->openGLImageTextures());
765
767}
768
769void KisCanvas2::createCanvas(bool useOpenGL)
770{
771 // deinitialize previous canvas structures
772 m_d->prescaledProjection = 0;
773 m_d->frameCache = 0;
774
775 KisConfig cfg(true);
776
777 if (useOpenGL && !KisOpenGL::hasOpenGL()) {
778 warnKrita << "Tried to create OpenGL widget when system doesn't have OpenGL\n";
779 useOpenGL = false;
780 }
781
782 if (useOpenGL) {
784 if (cfg.canvasState() == "OPENGL_FAILED") {
785 // Creating the opengl canvas failed, fall back
786 warnKrita << "OpenGL Canvas initialization returned OPENGL_FAILED. Falling back to QPainter.";
788 }
789 } else {
791 }
792
793 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->multiSurfaceState);
794 m_d->displayColorConverter.setMultiSurfaceDisplayConfig(m_d->multiSurfaceState->multiConfig);
795
796 if (m_d->popupPalette) {
797 m_d->popupPalette->setParent(m_d->canvasWidget->widget());
798 }
799
800}
801
803{
804 KisImageSP image = m_d->view->image();
805
806 m_d->displayColorConverter.setImageColorSpace(image->colorSpace());
807 m_d->coordinatesConverter->setImage(image);
808 m_d->toolProxy.initializeImage(image);
809
810 connect(image, SIGNAL(sigImageUpdated(QRect)), SLOT(startUpdateCanvasProjection(QRect)), Qt::DirectConnection);
811 connect(image->signalRouter(), SIGNAL(sigNotifyBatchUpdateStarted()), SLOT(slotBeginUpdatesBatch()), Qt::DirectConnection);
812 connect(image->signalRouter(), SIGNAL(sigNotifyBatchUpdateEnded()), SLOT(slotEndUpdatesBatch()), Qt::DirectConnection);
813 connect(image->signalRouter(), SIGNAL(sigRequestLodPlanesSyncBlocked(bool)), SLOT(slotSetLodUpdatesBlocked(bool)), Qt::DirectConnection);
814
815 connect(image, SIGNAL(sigProofingConfigChanged()), SLOT(slotChangeProofingConfig()));
816 connect(image, SIGNAL(sigSizeChanged(QPointF,QPointF)), SLOT(startResizingImage()), Qt::DirectConnection);
817 connect(image->undoAdapter(), SIGNAL(selectionChanged()), SLOT(slotTrySwitchShapeManager()));
818
819 connect(image, SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), SLOT(slotImageColorSpaceChanged()));
820 connect(image, SIGNAL(sigProfileChanged(const KoColorProfile*)), SLOT(slotImageColorSpaceChanged()));
821
824}
825
827{
828 KisImageSP image = m_d->view->image();
829
838 disconnect(image.data(), 0, this, 0);
839 image->unlock();
840}
841
843{
844 KisImageWSP image = m_d->view->image();
845
846 if (!m_d->currentCanvasIsOpenGL) {
847 Q_ASSERT(m_d->prescaledProjection);
848 m_d->prescaledProjection->setImage(image);
849 }
850
852 setLodPreferredInCanvas(m_d->lodPreferredInImage);
853
854 Q_EMIT sigCanvasEngineChanged();
855}
856
857void KisCanvas2::resetCanvas(bool useOpenGL)
858{
859 // we cannot reset the canvas before it's created, but this method might be called,
860 // for instance when setting the monitor profile.
861 if (!m_d->canvasWidget) {
862 return;
863 }
864
865 KisConfig cfg(true);
866
867 const bool canvasHasNativeSurface = bool(m_d->canvasWidget->widget()->windowHandle());
868 const bool canvasNeedsNativeSurface =
871
872 bool needReset = (m_d->currentCanvasIsOpenGL != useOpenGL) ||
873 (m_d->currentCanvasIsOpenGL &&
874 m_d->openGLFilterMode != cfg.openGLFilteringMode()) ||
875 canvasHasNativeSurface != canvasNeedsNativeSurface;
876
877 if (needReset) {
878 createCanvas(useOpenGL);
880 slotEffectiveZoomChanged(m_d->coordinatesConverter->effectiveZoom());
881 }
883}
884
885void KisCanvas2::startUpdateInPatches(const QRect &imageRect)
886{
892 if (m_d->currentCanvasIsOpenGL) {
894 } else {
895 KisImageConfig imageConfig(true);
896 int patchWidth = imageConfig.updatePatchWidth();
897 int patchHeight = imageConfig.updatePatchHeight();
898
899 for (int y = 0; y < imageRect.height(); y += patchHeight) {
900 for (int x = 0; x < imageRect.width(); x += patchWidth) {
901 QRect patchRect(x, y, patchWidth, patchHeight);
903 }
904 }
905 }
906}
907
909{
910 m_d->displayColorConverter.setDisplayFilter(displayFilter);
911 KisImageSP image = this->image();
912
913 m_d->view->viewManager()->blockUntilOperationsFinishedForced(image);
914
916 m_d->canvasWidget->setDisplayFilter(displayFilter);
917 image->unlock();
918}
919
921{
922 return m_d->displayColorConverter.displayFilter();
923}
924
926{
927 KisImageSP image = this->image();
928
929 m_d->view->viewManager()->blockUntilOperationsFinishedForced(image);
930
931 m_d->displayColorConverter.setImageColorSpace(image->colorSpace());
932 m_d->channelFlags = image->rootLayer()->channelFlags();
933 m_d->canvasWidget->channelSelectionChanged(m_d->channelFlags);
934
935 // Not all color spaces are supported by soft-proofing, so update state
936 if (imageView()->softProofing()) {
938 }
939
941 m_d->canvasWidget->notifyImageColorSpaceChanged(image->colorSpace());
942 image->unlock();
943}
944
946{
947 return &m_d->displayColorConverter;
948}
949
958
960{
962 if (!baseConfig) {
963 baseConfig = KisImageConfig(true).defaultProofingconfiguration();
964 }
965 *m_d->proofingConfig = *baseConfig;
966
968}
969
971{
972 KoColorConversionTransformation::ConversionFlags displayFlags = m_d->proofingConfig->displayFlags;
973 displayFlags.setFlag(KoColorConversionTransformation::SoftProofing, false);
974
975 if (image()->colorSpace()->colorDepthId().id().contains("U")) {
976 displayFlags.setFlag(KoColorConversionTransformation::SoftProofing, imageView()->softProofing());
977 displayFlags.setFlag(KoColorConversionTransformation::GamutCheck, imageView()->gamutCheck());
978 }
979 m_d->proofingConfig->displayFlags = displayFlags;
980 m_d->proofingConfigUpdated = true;
981
982 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->multiSurfaceState);
983 auto newState = m_d->multiSurfaceSetupManager.onProofingChanged(*m_d->multiSurfaceState, m_d->proofingConfig);
984 m_d->assignChangedMultiSurfaceState(newState);
985}
986
992
994{
996 if (imageView()->softProofing()) {
998 }
999}
1000
1002{
1003 if (image() && !image()->proofingConfiguration()) {
1004 // global config should be updated only when
1005 // the image doesn't have its own config
1007 }
1008}
1009
1011{
1013 if (imageView()->softProofing()) {
1015 }
1016}
1017
1019{
1020 m_d->proofingConfigUpdated = updated;
1021}
1022
1024{
1025 return m_d->proofingConfigUpdated;
1026}
1027
1029{
1030 return m_d->proofingConfig;
1031}
1032
1034{
1035 KisImageWSP image = this->image();
1036 qint32 w = image->width();
1037 qint32 h = image->height();
1038
1039 Q_EMIT sigContinueResizeImage(w, h);
1040
1041 QRect imageBounds(0, 0, w, h);
1042 startUpdateInPatches(imageBounds);
1043}
1044
1045void KisCanvas2::finishResizingImage(qint32 w, qint32 h)
1046{
1047 m_d->canvasWidget->finishResizingImage(w, h);
1048}
1049
1051{
1052 KisUpdateInfoSP info = m_d->canvasWidget->startUpdateCanvasProjection(rc);
1053 if (m_d->projectionUpdatesCompressor.putUpdateInfo(info)) {
1054 Q_EMIT sigCanvasCacheUpdated();
1055 }
1056}
1057
1059{
1060 auto tryIssueCanvasUpdates = [this](const QRect &vRect) {
1061 if (!m_d->isBatchUpdateActive) {
1062 // TODO: Implement info->dirtyViewportRect() for KisOpenGLCanvas2 to avoid updating whole canvas
1063 if (m_d->currentCanvasIsOpenGL) {
1064 m_d->savedCanvasProjectionUpdateRect |= vRect;
1065
1066 // we already had a compression in frameRenderStartCompressor, so force the update directly
1068 } else if (/* !m_d->currentCanvasIsOpenGL && */ !vRect.isEmpty()) {
1069 m_d->savedCanvasProjectionUpdateRect |= m_d->coordinatesConverter->viewportToWidget(vRect).toAlignedRect();
1070
1071 // we already had a compression in frameRenderStartCompressor, so force the update directly
1073 }
1074 }
1075 };
1076
1077 auto uploadData = [this, tryIssueCanvasUpdates](const QVector<KisUpdateInfoSP> &infoObjects) {
1078 QVector<QRect> viewportRects = m_d->canvasWidget->updateCanvasProjection(infoObjects);
1079 const QRect vRect = std::accumulate(viewportRects.constBegin(), viewportRects.constEnd(),
1080 QRect(), std::bit_or<QRect>());
1081
1082 tryIssueCanvasUpdates(vRect);
1083 };
1084
1085 bool shouldExplicitlyIssueUpdates = false;
1086
1087 QVector<KisUpdateInfoSP> infoObjects;
1088 KisUpdateInfoList originalInfoObjects;
1089 m_d->projectionUpdatesCompressor.takeUpdateInfo(originalInfoObjects);
1090
1091 for (auto it = originalInfoObjects.constBegin();
1092 it != originalInfoObjects.constEnd();
1093 ++it) {
1094
1095 KisUpdateInfoSP info = *it;
1096
1097 const KisMarkerUpdateInfo *batchInfo = dynamic_cast<const KisMarkerUpdateInfo*>(info.data());
1098 if (batchInfo) {
1099 if (!infoObjects.isEmpty()) {
1100 uploadData(infoObjects);
1101 infoObjects.clear();
1102 }
1103
1104 if (batchInfo->type() == KisMarkerUpdateInfo::StartBatch) {
1105 m_d->isBatchUpdateActive++;
1106 } else if (batchInfo->type() == KisMarkerUpdateInfo::EndBatch) {
1107 m_d->isBatchUpdateActive--;
1108 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->isBatchUpdateActive >= 0);
1109 if (m_d->isBatchUpdateActive == 0) {
1110 shouldExplicitlyIssueUpdates = true;
1111 }
1112 } else if (batchInfo->type() == KisMarkerUpdateInfo::BlockLodUpdates) {
1113 m_d->canvasWidget->setLodResetInProgress(true);
1114 } else if (batchInfo->type() == KisMarkerUpdateInfo::UnblockLodUpdates) {
1115 m_d->canvasWidget->setLodResetInProgress(false);
1116 shouldExplicitlyIssueUpdates = true;
1117 }
1118 } else {
1119 infoObjects << info;
1120 }
1121 }
1122
1123 if (!infoObjects.isEmpty()) {
1124 uploadData(infoObjects);
1125 } else if (shouldExplicitlyIssueUpdates) {
1126 tryIssueCanvasUpdates(m_d->coordinatesConverter->imageRectInImagePixels());
1127 }
1128}
1129
1131{
1132 KisUpdateInfoSP info =
1134 m_d->coordinatesConverter->imageRectInImagePixels());
1135 m_d->projectionUpdatesCompressor.putUpdateInfo(info);
1136 Q_EMIT sigCanvasCacheUpdated();
1137}
1138
1140{
1141 KisUpdateInfoSP info =
1143 m_d->coordinatesConverter->imageRectInImagePixels());
1144 m_d->projectionUpdatesCompressor.putUpdateInfo(info);
1145 Q_EMIT sigCanvasCacheUpdated();
1146}
1147
1149{
1150 KisUpdateInfoSP info =
1154 m_d->coordinatesConverter->imageRectInImagePixels());
1155 m_d->projectionUpdatesCompressor.putUpdateInfo(info);
1156 Q_EMIT sigCanvasCacheUpdated();
1157}
1158
1160{
1165#if !KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
1166 m_d->canvasUpdateCompressor.start();
1167#else
1169#endif
1170}
1171
1173{
1174
1175#if !KRITA_QT_HAS_UPDATE_COMPRESSION_PATCH
1181 if (m_d->canvasWidget->isBusy()) {
1182 // just restarting the timer
1183 m_d->canvasUpdateCompressor.start();
1184 return;
1185 }
1186#endif
1187
1188 QRect combinedUpdateRect = m_d->savedCanvasProjectionUpdateRect | m_d->savedOverlayUpdateRect;
1189 if (!combinedUpdateRect.isEmpty()) {
1190 // TODO: Remove this signal (only used by the old KisSketchView)
1191 Q_EMIT updateCanvasRequested(combinedUpdateRect);
1192
1193 if (wrapAroundViewingMode() && !m_d->savedCanvasProjectionUpdateRect.isEmpty()) {
1194 const QRect widgetRect = m_d->canvasWidget->widget()->rect();
1195 const QRect imageRect = m_d->coordinatesConverter->imageRectInImagePixels();
1196
1197 const QRect widgetRectInImagePixels =
1198 m_d->coordinatesConverter->widgetToImage(widgetRect).toAlignedRect();
1199
1200 const QRect rc = m_d->coordinatesConverter->widgetToImage(m_d->savedCanvasProjectionUpdateRect).toAlignedRect();
1201
1202 const QVector<QRect> updateRects =
1203 KisWrappedRect::multiplyWrappedRect(rc, imageRect, widgetRectInImagePixels, wrapAroundViewingModeAxis());
1204
1205 Q_FOREACH(const QRect &rc, updateRects) {
1206 const QRect widgetUpdateRect =
1207 m_d->coordinatesConverter->imageToWidget(rc).toAlignedRect() & widgetRect;
1208 m_d->canvasWidget->updateCanvasImage(widgetUpdateRect);
1209 }
1210 m_d->canvasWidget->updateCanvasDecorations(m_d->savedOverlayUpdateRect);
1211 } else {
1212 m_d->canvasWidget->updateCanvasImage(m_d->savedCanvasProjectionUpdateRect);
1213 m_d->canvasWidget->updateCanvasDecorations(m_d->savedOverlayUpdateRect);
1214 }
1215 } else if (m_d->updateSceneRequested) {
1216 m_d->canvasWidget->widget()->update();
1217 }
1218
1219 m_d->savedCanvasProjectionUpdateRect = QRect();
1220 m_d->savedOverlayUpdateRect = QRect();
1221 m_d->updateSceneRequested = false;
1222}
1223
1225{
1226 QRect rect = m_d->canvasWidget->widget()->rect();
1227 if (!rc.isEmpty()) {
1228 rect &= rc;
1229 if (rect.isEmpty()) {
1230 return;
1231 }
1232 }
1233 // We don't know if it's the canvas projection or the overlay that's
1234 // changed, so we update both.
1235 m_d->savedCanvasProjectionUpdateRect |= rect;
1236 m_d->savedOverlayUpdateRect |= rect;
1238}
1239
1244
1245QRect KisCanvas2::KisCanvas2Private::docUpdateRectToWidget(const QRectF &docRect)
1246{
1247 QRect widgetRect = coordinatesConverter->documentToWidget(docRect).toAlignedRect();
1248 widgetRect.adjust(-2, -2, 2, 2);
1249 return widgetRect & canvasWidget->widget()->rect();
1250}
1251
1252void KisCanvas2::updateCanvas(const QRectF& documentRect)
1253{
1254 // updateCanvas is called from tools, never from the projection
1255 // updates, so no need to prescale!
1256 QRect widgetRect = m_d->docUpdateRectToWidget(documentRect);
1257 if (!widgetRect.isEmpty()) {
1258 updateCanvasWidgetImpl(widgetRect);
1259 }
1260}
1261
1262void KisCanvas2::updateCanvasProjection(const QRectF &docRect)
1263{
1264 QRect widgetRect = m_d->docUpdateRectToWidget(docRect);
1265 if (!widgetRect.isEmpty()) {
1266 m_d->savedCanvasProjectionUpdateRect |= widgetRect;
1268 }
1269}
1270
1272{
1273 m_d->savedOverlayUpdateRect = m_d->canvasWidget->widget()->rect();
1275}
1276
1277void KisCanvas2::updateCanvasDecorations(const QRectF &docRect)
1278{
1279 QRect widgetRect = m_d->docUpdateRectToWidget(docRect);
1280 if (!widgetRect.isEmpty()) {
1281 m_d->savedOverlayUpdateRect |= widgetRect;
1283 }
1284}
1285
1287{
1288 QRect widgetRect = m_d->docUpdateRectToWidget(docRect);
1289 if (!widgetRect.isEmpty()) {
1290 updateCanvasToolOutlineWdg(widgetRect);
1291 }
1292}
1293
1294void KisCanvas2::updateCanvasToolOutlineWdg(const QRect &widgetRect)
1295{
1296 QRect rect = widgetRect & m_d->canvasWidget->widget()->rect();
1297 if (!rect.isEmpty()) {
1298 m_d->savedOverlayUpdateRect |= rect;
1299#ifdef HAVE_NO_QT_UPDATE_COMPRESSIO
1300 m_d->canvasUpdateCompressor.start();
1301#else
1303#endif
1304 }
1305}
1306
1308{
1309 m_d->updateSceneRequested = true;
1311}
1312
1314{
1316 m_d->view->disconnect(object);
1317}
1318
1320{
1321 Q_UNUSED(newZoom)
1322
1323
1324
1326}
1327
1328QRect KisCanvas2::regionOfInterest() const
1329{
1330 return m_d->regionOfInterest;
1331}
1332
1334{
1335 const QRect oldRegionOfInterest = m_d->regionOfInterest;
1336
1337 const qreal ratio = m_d->regionOfInterestMargin;
1338 const QRect proposedRoi = KisAlgebra2D::blowRect(m_d->coordinatesConverter->widgetRectInImagePixels(), ratio).toAlignedRect();
1339
1340 const QRect imageRect = m_d->coordinatesConverter->imageRectInImagePixels();
1341
1342 m_d->regionOfInterest = proposedRoi & imageRect;
1343
1344 if (m_d->regionOfInterest != oldRegionOfInterest) {
1345 Q_EMIT sigRegionOfInterestChanged(m_d->regionOfInterest);
1346 }
1347}
1348
1350{
1351 QRectF referencesRect;
1352 KisReferenceImagesLayerSP layer = m_d->view->document()->referenceImagesLayer();
1353 if (layer) {
1354 referencesRect = layer->boundingImageRect();
1355 }
1356
1357 m_d->view->canvasController()->syncOnReferencesChange(referencesRect);
1358}
1359
1361{
1362 m_d->renderingLimit = rc;
1363}
1364
1365QRect KisCanvas2::renderingLimit() const
1366{
1367 return m_d->renderingLimit;
1368}
1369
1371{
1372 return m_d->popupPalette;
1373}
1374
1376{
1377 // noop, the shape manager is recalculated lazily on the fly
1378}
1379
1381{
1382 KisImageSP image = this->image();
1383
1384 if (m_d->bootstrapLodBlocked || !m_d->lodIsSupported()) {
1386 } else {
1387 const qreal effectiveZoom = m_d->coordinatesConverter->effectiveZoom();
1388
1389 KisConfig cfg(true);
1390 const int maxLod = cfg.numMipmapLevels();
1391 const int lod = KisLodTransform::scaleToLod(effectiveZoom, maxLod);
1392 KisLodPreferences::PreferenceFlags flags = KisLodPreferences::LodSupported;
1393
1394 if (m_d->lodPreferredInImage) {
1396 }
1398 }
1399}
1400
1402{
1403 if (m_d->view) {
1404 return m_d->view->viewManager();
1405 }
1406 return 0;
1407}
1408
1410{
1411 return m_d->view;
1412}
1413
1415{
1416 return m_d->view->image();
1417
1418}
1419
1421{
1422 return m_d->view->image();
1423}
1424
1426{
1427 if (!m_d->currentCanvasIsOpenGL) {
1428 Q_ASSERT(m_d->prescaledProjection);
1429 auto state = KisCanvasState::fromConverter(*m_d->coordinatesConverter);
1430 m_d->prescaledProjection->notifyCanvasStateChanged(state);
1431 }
1432
1433 updateCanvas();
1434 m_d->regionOfInterestUpdateCompressor.start();
1435
1436 Q_EMIT sigCanvasStateChanged();
1437}
1438
1440{
1441 KisConfig cfg(true);
1442 m_d->regionOfInterestMargin = KisImageConfig(true).animationCacheRegionOfInterestMargin();
1443
1444 resetCanvas(cfg.useOpenGL());
1445
1446 QWidget *mainWindow = m_d->view->mainWindow();
1448
1449 QWidget *topLevelWidget = mainWindow->topLevelWidget();
1450 KIS_SAFE_ASSERT_RECOVER_RETURN(topLevelWidget);
1451
1452 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->multiSurfaceState);
1453 auto newState = m_d->multiSurfaceSetupManager.onConfigChanged(*m_d->multiSurfaceState,
1454 m_d->currentScreenId(),
1457 m_d->assignChangedMultiSurfaceState(newState);
1458
1460}
1461
1463{
1464 const int screenId = qApp->screens().indexOf(screen);
1465
1466 if (screenId < 0) {
1467 warnUI << "Failed to get screenNumber for updating display profile.";
1468 return;
1469 }
1470
1471 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->multiSurfaceState);
1472 auto newState = m_d->multiSurfaceSetupManager.onScreenChanged(*m_d->multiSurfaceState,
1473 screenId);
1474 m_d->assignChangedMultiSurfaceState(newState);
1475}
1476
1478{
1479 KisImageSP image = this->image();
1480 KisImageReadOnlyBarrierLock l(image);
1482}
1483
1484void KisCanvas2::KisCanvas2Private::assignChangedMultiSurfaceStateSkipCanvasSurface(const KisMultiSurfaceStateManager::State &newState)
1485{
1486 // the surface state is supposed to be initialized on canvas creation
1487 KIS_SAFE_ASSERT_RECOVER_RETURN(multiSurfaceState);
1488
1489 if (*multiSurfaceState == newState) return;
1490
1491 const KisMultiSurfaceStateManager::State oldState = *this->multiSurfaceState;
1492 this->multiSurfaceState = newState;
1493
1494 displayColorConverter.setMultiSurfaceDisplayConfig(newState.multiConfig);
1495
1496 if (oldState.multiConfig.canvasDisplayConfig() != newState.multiConfig.canvasDisplayConfig()) {
1497 KisImageSP image = view->image();
1498 KisImageReadOnlyBarrierLock l(image);
1499 canvasWidget->setDisplayConfig(multiSurfaceState->multiConfig.canvasDisplayConfig());
1500
1501 q->refetchDataFromImage();
1502
1503 // we changed the canvas conversion mode, so the canvas background color
1504 // has changed as well
1505 q->updateCanvas();
1506 }
1507}
1508
1509void KisCanvas2::KisCanvas2Private::assignChangedMultiSurfaceState(const KisMultiSurfaceStateManager::State &newState)
1510{
1511 assignChangedMultiSurfaceStateSkipCanvasSurface(newState);
1512
1513#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
1514
1515 if (surfaceColorManager) {
1516 surfaceColorManager->setDisplayConfigOptions(newState.surfaceMode, newState.multiConfig.options());
1517 }
1518
1519#endif
1520}
1521
1523{
1524#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
1525
1526 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->multiSurfaceState);
1527 if (m_d->multiSurfaceState->multiConfig.canvasDisplayConfig() == config) return;
1528
1529 if (m_d->multiSurfaceState->isCanvasOpenGL) {
1530 if (config.isHDR &&
1531 m_d->canvasWidget->currentBitDepthMode() < KisOpenGLCanvas2::BitDepthMode::Depth10Bit) {
1532
1533 const QString warningMessage = i18n(
1534 "WARNING: HDR mode was activated on surface working in 8-bit mode!\n"
1535 "Please activate 10-bit mode in Krita's Preferences dialog and restart "
1536 "Krita to avoid color banding!");
1537
1538 m_d->view->showFloatingMessage(warningMessage, koIcon("warning"), 7000, KisFloatingMessage::High);
1539 warnOpenGL.noquote() << QString(warningMessage).replace('\n', ' ');
1540 warnOpenGL << ppVar(QSurfaceFormat::defaultFormat());
1541 }
1542 }
1543
1544 auto newState = m_d->multiSurfaceSetupManager.onCanvasSurfaceFormatChanged(*m_d->multiSurfaceState, config);
1545 m_d->assignChangedMultiSurfaceStateSkipCanvasSurface(newState);
1546#endif
1547}
1548
1550{
1551 m_d->canvasWidget->addDecoration(deco);
1552}
1553
1555{
1556 return m_d->canvasWidget->decoration(id);
1557}
1558
1559
1561{
1568 return QPoint();
1569}
1570
1572{
1573 return m_d->coordinatesConverter->documentOffset();
1574}
1575
1577{
1578 m_d->popupPalette = new KisPopupPalette(viewManager(), m_d->coordinatesConverter, favoriteResourceManager, displayColorConverter()->displayRendererInterface(),
1579 m_d->canvasWidget->widget());
1580 connect(m_d->popupPalette, SIGNAL(zoomLevelChanged(int)), this, SLOT(slotPopupPaletteRequestedZoomChange(int)));
1581 connect(m_d->popupPalette, SIGNAL(sigUpdateCanvas()), this, SLOT(updateCanvas()));
1582 connect(m_d->view->mainWindow(), SIGNAL(themeChanged()), m_d->popupPalette, SLOT(slotUpdateIcons()));
1583}
1584
1586 m_d->view->canvasController()->setZoom(KoZoomMode::ZOOM_CONSTANT, (qreal)(zoom/100.0)); // 1.0 is 100% zoom
1587}
1588
1589void KisCanvas2::setCursor(const QCursor &cursor)
1590{
1591 canvasWidget()->setCursor(cursor);
1592}
1593
1595{
1596 return m_d->frameCache;
1597}
1598
1600{
1601 return m_d->animationPlayer.data();
1602}
1603
1605{
1606 KisShapeLayer* shapeLayer = dynamic_cast<KisShapeLayer*>(viewManager()->activeLayer().data());
1607 if (!shapeLayer) {
1608 return;
1609 }
1610 m_d->shapeManager.selection()->deselectAll();
1611 Q_FOREACH (KoShape* shape, shapeLayer->shapeManager()->selection()->selectedShapes()) {
1612 m_d->shapeManager.selection()->select(shape);
1613 }
1614}
1615
1617{
1618 KisCanvasDecorationSP infinityDecoration =
1619 m_d->canvasWidget->decoration(INFINITY_DECORATION_ID);
1620
1621 if (infinityDecoration) {
1622 infinityDecoration->setVisible(!value);
1623 }
1624
1625 m_d->canvasWidget->setWrapAroundViewingMode(value);
1626}
1627
1629{
1630 return m_d->canvasWidget->wrapAroundViewingMode();
1631}
1632
1634{
1635 m_d->canvasWidget->setWrapAroundViewingModeAxis(value);
1636 updateCanvas();
1637}
1638
1640{
1641 return m_d->canvasWidget->wrapAroundViewingModeAxis();
1642}
1643
1645{
1646 if (!m_d->bootstrapLodBlocked) return;
1647
1648 m_d->bootstrapLodBlocked = false;
1649 setLodPreferredInCanvas(m_d->lodPreferredInImage);
1650
1651 // Initialization of audio tracks is deferred until after canvas has been completely constructed.
1652 m_d->animationPlayer->setupAudioTracks();
1653}
1654
1656{
1657 if (!KisOpenGL::supportsLoD()) {
1658 qWarning() << "WARNING: Level of Detail functionality is available only with openGL + GLSL 1.3 support";
1659 }
1660
1661 m_d->lodPreferredInImage =
1662 value && m_d->lodIsSupported();
1663
1665
1666 KisConfig cfg(false);
1667 cfg.setLevelOfDetailEnabled(m_d->lodPreferredInImage);
1668}
1669
1671{
1672 return m_d->lodPreferredInImage;
1673}
1674
1676{
1677 KisCanvasDecorationSP deco = decoration("paintingAssistantsDecoration");
1678 return qobject_cast<KisPaintingAssistantsDecoration*>(deco.data());
1679}
1680
1682{
1683 KisCanvasDecorationSP deco = decoration("referenceImagesDecoration");
1684 return qobject_cast<KisReferenceImagesDecoration*>(deco.data());
1685}
1686
1688{
1689 return m_d->inputActionGroupsMaskInterface;
1690}
1691
1693{
1694#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
1695 QString report;
1696 QDebug str(&report);
1697
1698 if (m_d->canvasWidget) {
1699 str << "(canvas bit depth report)" << Qt::endl;
1700 str << Qt::endl;
1701 str.noquote().nospace() << m_d->canvasWidget->currentBitDepthUserReport();
1702 }
1703
1704 str << Qt::endl;
1705
1706 if (m_d->surfaceColorManager) {
1707 str.noquote().nospace() << QString("(canvas surface color manager)\n");
1708 str.noquote().nospace() << QString("\n");
1709 str.noquote().nospace() << m_d->surfaceColorManager->colorManagementReport();
1710 } else {
1711 str.noquote().nospace() << QString("Surface color management is not supported on this platform\n");
1712 }
1713
1714 return report;
1715#else
1716 return "Surface color management is disabled\n";
1717#endif
1718}
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()
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:805
void barrierLock(bool readOnly=false)
Wait until all the queued background jobs are completed and lock the image.
Definition kis_image.cc:756
qint32 width() const
void immediateLockForReadOnly()
Definition kis_image.cc:793
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:1157
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)