diff --git a/krita/krita4.xmlgui b/krita/krita4.xmlgui --- a/krita/krita4.xmlgui +++ b/krita/krita4.xmlgui @@ -73,7 +73,17 @@ &View - + + + + Wraparound Mode + + + + + + + diff --git a/krita/kritamenu.action b/krita/kritamenu.action --- a/krita/kritamenu.action +++ b/krita/kritamenu.action @@ -597,6 +597,36 @@ true + + + + &Show Horizontal Wrap Around + + Show Horizontal Wrap Around + Show Horizontal Wrap Around + 1 + 0 + + true + + + + + + &Show Vertical Wrap Around + + Show Vertical Wrap Around + Show Vertical Wrap Around + 1 + 0 + + true + + + + + + &Instant Preview Mode diff --git a/libs/global/kis_config_notifier.h b/libs/global/kis_config_notifier.h --- a/libs/global/kis_config_notifier.h +++ b/libs/global/kis_config_notifier.h @@ -47,6 +47,7 @@ void notifyDropFramesModeChanged(); void notifyPixelGridModeChanged(); + void notifyWraparoundModeChanged(); Q_SIGNALS: /** @@ -55,6 +56,7 @@ void configChanged(void); void dropFramesModeChanged(); void pixelGridModeChanged(); + void wraparoundModeChanged(); private: KisConfigNotifier(const KisConfigNotifier&); KisConfigNotifier operator=(const KisConfigNotifier&); diff --git a/libs/global/kis_config_notifier.cpp b/libs/global/kis_config_notifier.cpp --- a/libs/global/kis_config_notifier.cpp +++ b/libs/global/kis_config_notifier.cpp @@ -61,3 +61,9 @@ { emit pixelGridModeChanged(); } + +void KisConfigNotifier::notifyWraparoundModeChanged() +{ + emit wraparoundModeChanged(); +} + diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp --- a/libs/ui/KisViewManager.cpp +++ b/libs/ui/KisViewManager.cpp @@ -165,6 +165,8 @@ , rotateCanvasLeft(0) , resetCanvasRotation(0) , wrapAroundAction(0) + , wrapAroundHorizontalAction(0) + , wrapAroundVerticalAction(0) , levelOfDetailAction(0) , showRulersAction(0) , rulersTrackMouseAction(0) @@ -206,6 +208,8 @@ KisAction *rotateCanvasLeft; KisAction *resetCanvasRotation; KisAction *wrapAroundAction; + KisAction *wrapAroundHorizontalAction; + KisAction *wrapAroundVerticalAction; KisAction *levelOfDetailAction; KisAction *showRulersAction; KisAction *rulersTrackMouseAction; @@ -459,6 +463,12 @@ d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool))); d->wrapAroundAction->setChecked(canvasController->wrapAroundMode()); + + d->viewConnections.addUniqueConnection(d->wrapAroundHorizontalAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundModeHorizontal(bool))); + d->viewConnections.addUniqueConnection(d->wrapAroundVerticalAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundModeVertical(bool))); + + + d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool))); d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode()); @@ -692,6 +702,13 @@ d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left"); d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation"); d->wrapAroundAction = actionManager()->createAction("wrap_around_mode"); + + d->wrapAroundHorizontalAction = actionManager()->createAction("wrap_around_mode_horizontal"); + d->wrapAroundHorizontalAction->setChecked(cfg.wraparoundModeHorizontalEnabled()); + + d->wrapAroundVerticalAction = actionManager()->createAction("wrap_around_mode_vertical"); + d->wrapAroundVerticalAction->setChecked(cfg.wraparoundModeVerticalEnabled()); + d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode"); d->softProof = actionManager()->createAction("softProof"); d->gamutCheck = actionManager()->createAction("gamutCheck"); diff --git a/libs/ui/canvas/kis_canvas_controller.h b/libs/ui/canvas/kis_canvas_controller.h --- a/libs/ui/canvas/kis_canvas_controller.h +++ b/libs/ui/canvas/kis_canvas_controller.h @@ -41,6 +41,7 @@ bool eventFilter(QObject *watched, QEvent *event) override; void updateDocumentSize(const QSize &sz, bool recalculateCenter) override; void activate() override; + virtual void changeCanvasWidget(QWidget* widget) override; QPointF currentCursorPosition() const override; @@ -63,6 +64,8 @@ qreal rotation() const; void resetCanvasRotation(); void slotToggleWrapAroundMode(bool value); + void slotToggleWrapAroundModeHorizontal(bool value); + void slotToggleWrapAroundModeVertical(bool value); void slotTogglePixelGrid(bool value); void slotToggleLevelOfDetailMode(bool value); diff --git a/libs/ui/canvas/kis_canvas_controller.cpp b/libs/ui/canvas/kis_canvas_controller.cpp --- a/libs/ui/canvas/kis_canvas_controller.cpp +++ b/libs/ui/canvas/kis_canvas_controller.cpp @@ -97,6 +97,7 @@ m_d(new Private(this)) { m_d->view = parent; + } KisCanvasController::~KisCanvasController() @@ -130,6 +131,18 @@ KoCanvasControllerWidget::activate(); } +void KisCanvasController::changeCanvasWidget(QWidget* widget) +{ + KoCanvasControllerWidget::changeCanvasWidget(widget); + + KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); + Q_ASSERT(kritaCanvas); + + if (canvas()->canvasIsOpenGL() ) { + kritaCanvas->setWrapAroundViewingMode(wrapAroundMode()); + } +} + QPointF KisCanvasController::currentCursorPosition() const { KoCanvasBase *canvas = m_d->view->canvasBase(); @@ -279,6 +292,22 @@ return kritaCanvas->wrapAroundViewingMode(); } +void KisCanvasController::slotToggleWrapAroundModeHorizontal(bool value) +{ + KisConfig cfg(false); + cfg.enableWraparoundModeHorizontal(value); + + KisConfigNotifier::instance()->notifyWraparoundModeChanged(); +} + +void KisCanvasController::slotToggleWrapAroundModeVertical(bool value) +{ + KisConfig cfg(false); + cfg.setWraparoundModeHorizontalEnabled(value); + + KisConfigNotifier::instance()->notifyWraparoundModeChanged(); +} + void KisCanvasController::slotTogglePixelGrid(bool value) { KisConfig cfg(false); diff --git a/libs/ui/kis_config.h b/libs/ui/kis_config.h --- a/libs/ui/kis_config.h +++ b/libs/ui/kis_config.h @@ -216,6 +216,12 @@ bool pixelGridEnabled(bool defaultValue = false) const; void enablePixelGrid(bool v) const; + bool wraparoundModeHorizontalEnabled(bool defaultValue = false) const; + void enableWraparoundModeHorizontal(bool v) const; + + bool wraparoundModeVerticalEnabled(bool defaultValue = false) const; + void setWraparoundModeHorizontalEnabled(bool v) const; + quint32 guidesLineStyle(bool defaultValue = false) const; void setGuidesLineStyle(quint32 v) const; QColor guidesColor(bool defaultValue = false) const; diff --git a/libs/ui/kis_config.cc b/libs/ui/kis_config.cc --- a/libs/ui/kis_config.cc +++ b/libs/ui/kis_config.cc @@ -809,6 +809,29 @@ m_cfg.writeEntry("pixelGridEnabled", v); } +void KisConfig::enableWraparoundModeHorizontal(bool v) const +{ + m_cfg.writeEntry("wraparoundModeHorizontal", v); +} + +bool KisConfig::wraparoundModeHorizontalEnabled(bool defaultValue) const +{ + bool enabled = true; + return (defaultValue ? enabled : m_cfg.readEntry("wraparoundModeHorizontal", enabled)); +} + +void KisConfig::setWraparoundModeHorizontalEnabled(bool v) const +{ + m_cfg.writeEntry("wraparoundModeVertical", v); +} + + +bool KisConfig::wraparoundModeVerticalEnabled(bool defaultValue) const +{ + bool enabled = true; + return (defaultValue ? enabled : m_cfg.readEntry("wraparoundModeVertical", enabled)); +} + quint32 KisConfig::guidesLineStyle(bool defaultValue) const { int v = m_cfg.readEntry("guidesLineStyle", 0); diff --git a/libs/ui/opengl/kis_opengl_canvas2.h b/libs/ui/opengl/kis_opengl_canvas2.h --- a/libs/ui/opengl/kis_opengl_canvas2.h +++ b/libs/ui/opengl/kis_opengl_canvas2.h @@ -105,6 +105,7 @@ public Q_SLOTS: void slotConfigChanged(); void slotPixelGridModeChanged(); + void slotWraparoundModeChanged(); protected: // KisCanvasWidgetBase bool callFocusNextPrevChild(bool next) override; @@ -117,6 +118,7 @@ void drawImage(); void drawCheckers(); void drawGrid(); + void updateActiveCanvasArea(); private: diff --git a/libs/ui/opengl/kis_opengl_canvas2.cpp b/libs/ui/opengl/kis_opengl_canvas2.cpp --- a/libs/ui/opengl/kis_opengl_canvas2.cpp +++ b/libs/ui/opengl/kis_opengl_canvas2.cpp @@ -91,6 +91,9 @@ bool wrapAroundMode{false}; + QRect activeCanvasArea; // changes depending on wrap-around mode + QRectF canvasWidgetRect; + // Stores a quad for drawing the canvas QOpenGLVertexArrayObject quadVAO; QOpenGLBuffer quadBuffers[2]; @@ -111,6 +114,10 @@ QColor gridColor; QColor cursorColor; + // individually turn on/off horizontal or vertical aspect of wrap-around mode + bool isHorizontalWrapAround = true; + bool isVerticalWrapAround = true; + bool lodSwitchInProgress = false; int xToColWithWrapCompensation(int x, const QRect &imageRect) { @@ -172,6 +179,9 @@ connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotPixelGridModeChanged())); + connect(KisConfigNotifier::instance(), SIGNAL(wraparoundModeChanged()), SLOT(slotWraparoundModeChanged())); + + slotConfigChanged(); slotPixelGridModeChanged(); cfg.writeEntry("canvasState", "OPENGL_SUCCESS"); @@ -207,6 +217,7 @@ void KisOpenGLCanvas2::setWrapAroundViewingMode(bool value) { d->wrapAroundMode = value; + slotWraparoundModeChanged(); update(); } @@ -488,14 +499,12 @@ QRectF textureRect; QRectF modelRect; - QRectF viewportRect = !d->wrapAroundMode ? - converter->imageRectInViewportPixels() : - converter->widgetToViewport(this->rect()); + updateActiveCanvasArea(); + QRectF viewportRect = d->activeCanvasArea; + + const QRect vrect = converter->imageToViewport(canvas()->renderingLimit()).toAlignedRect(); + viewportRect = vrect; - if (!canvas()->renderingLimit().isEmpty()) { - const QRect vrect = converter->imageToViewport(canvas()->renderingLimit()).toAlignedRect(); - viewportRect &= vrect; - } converter->getOpenGLCheckersInfo(viewportRect, &textureTransform, &modelTransform, &textureRect, &modelRect, d->scrollCheckers); @@ -552,6 +561,42 @@ glBindBuffer(GL_ARRAY_BUFFER, 0); } +void KisOpenGLCanvas2::updateActiveCanvasArea() +{ + QRect imageRect = d->openGLImageTextures->storedImageBounds(); // original image + + QRectF widgetRect(0,0, width(), height()); + QRectF widgetRectInImagePixels = coordinatesConverter()->documentToImage(coordinatesConverter()->widgetToDocument(widgetRect)); + QRect wigetAlignedRect = widgetRectInImagePixels.toAlignedRect(); // full canvas used for wrap-around mode + + if (!d->wrapAroundMode ) { + d->activeCanvasArea = imageRect; + d->canvasWidgetRect = imageRect; + return; + } else if( d->wrapAroundMode && d->isVerticalWrapAround == false && d->isHorizontalWrapAround == false ) { + d->activeCanvasArea = imageRect; + d->canvasWidgetRect = imageRect; + return; + } + + // has some form of wrap-around mode enabled + if (d->isVerticalWrapAround == true && d->isHorizontalWrapAround == false) { + // only wrap vertically + d->activeCanvasArea = QRect(imageRect.x(), wigetAlignedRect.y(), imageRect.width(), wigetAlignedRect.height() ); + d->canvasWidgetRect = QRectF(imageRect.x(), imageRect.y(), wigetAlignedRect.width(), imageRect.height()); + + } else if (d->isVerticalWrapAround == false && d->isHorizontalWrapAround == true) { + // only wrap horizontally + d->activeCanvasArea = QRect(wigetAlignedRect.x(), imageRect.y(), wigetAlignedRect.width(), imageRect.height() ); + d->canvasWidgetRect = QRectF(d->canvasWidgetRect.x(), imageRect.y(), d->canvasWidgetRect.width(), imageRect.height()); + } + else { + // display both vertical and horizontal + d->activeCanvasArea = wigetAlignedRect; + } +} + + void KisOpenGLCanvas2::drawGrid() { if (!d->solidColorShader->bind()) { @@ -580,18 +625,13 @@ d->lineBuffer.bind(); } - QRectF widgetRect(0,0, width(), height()); - QRectF widgetRectInImagePixels = coordinatesConverter()->documentToImage(coordinatesConverter()->widgetToDocument(widgetRect)); - QRect wr = widgetRectInImagePixels.toAlignedRect(); - - if (!d->wrapAroundMode) { - wr &= d->openGLImageTextures->storedImageBounds(); - } + updateActiveCanvasArea(); - QPoint topLeftCorner = wr.topLeft(); - QPoint bottomRightCorner = wr.bottomRight() + QPoint(1, 1); + QPoint topLeftCorner = d->activeCanvasArea.topLeft(); + QPoint bottomRightCorner = d->activeCanvasArea.bottomRight() + QPoint(1, 1); QVector grid; + for (int i = topLeftCorner.x(); i <= bottomRightCorner.x(); ++i) { grid.append(QVector3D(i, topLeftCorner.y(), 0)); grid.append(QVector3D(i, bottomRightCorner.y(), 0)); @@ -662,24 +702,21 @@ d->displayShader->setUniformValue(d->displayShader->location(Uniform::ViewportScale), (GLfloat) scaleX); d->displayShader->setUniformValue(d->displayShader->location(Uniform::TexelSize), (GLfloat) d->openGLImageTextures->texelSize()); - QRect ir = d->openGLImageTextures->storedImageBounds(); - QRect wr = widgetRectInImagePixels.toAlignedRect(); + updateActiveCanvasArea(); - if (!d->wrapAroundMode) { - // if we don't want to paint wrapping images, just limit the - // processing area, and the code will handle all the rest - wr &= ir; - } + QRect imageRect = d->openGLImageTextures->storedImageBounds(); // original image + + int firstColumn = d->xToColWithWrapCompensation(d->activeCanvasArea.left(), imageRect); + int lastColumn = d->xToColWithWrapCompensation(d->activeCanvasArea.right(), imageRect); - int firstColumn = d->xToColWithWrapCompensation(wr.left(), ir); - int lastColumn = d->xToColWithWrapCompensation(wr.right(), ir); - int firstRow = d->yToRowWithWrapCompensation(wr.top(), ir); - int lastRow = d->yToRowWithWrapCompensation(wr.bottom(), ir); + int firstRow = d->yToRowWithWrapCompensation(d->activeCanvasArea.top(), imageRect); + int lastRow = d->yToRowWithWrapCompensation(d->activeCanvasArea.bottom(), imageRect); - int minColumn = d->openGLImageTextures->xToCol(ir.left()); - int maxColumn = d->openGLImageTextures->xToCol(ir.right()); - int minRow = d->openGLImageTextures->yToRow(ir.top()); - int maxRow = d->openGLImageTextures->yToRow(ir.bottom()); + + int minColumn = d->openGLImageTextures->xToCol(imageRect.left()); + int maxColumn = d->openGLImageTextures->xToCol(imageRect.right()); + int minRow = d->openGLImageTextures->yToRow(imageRect.top()); + int maxRow = d->openGLImageTextures->yToRow(imageRect.bottom()); int imageColumns = maxColumn - minColumn + 1; int imageRows = maxRow - minRow + 1; @@ -689,20 +726,21 @@ int effectiveCol = col; int effectiveRow = row; + QPointF tileWrappingTranslation; if (effectiveCol > maxColumn || effectiveCol < minColumn) { int translationStep = floor(qreal(col) / imageColumns); int originCol = translationStep * imageColumns; effectiveCol = col - originCol; - tileWrappingTranslation.rx() = translationStep * ir.width(); + tileWrappingTranslation.rx() = translationStep * imageRect.width(); } if (effectiveRow > maxRow || effectiveRow < minRow) { int translationStep = floor(qreal(row) / imageRows); int originRow = translationStep * imageRows; effectiveRow = row - originRow; - tileWrappingTranslation.ry() = translationStep * ir.height(); + tileWrappingTranslation.ry() = translationStep * imageRect.height(); } KisTextureTile *tile = @@ -831,6 +869,15 @@ update(); } +void KisOpenGLCanvas2::slotWraparoundModeChanged() +{ + KisConfig cfg(true); + + d->isHorizontalWrapAround = cfg.wraparoundModeHorizontalEnabled(); + d->isVerticalWrapAround = cfg.wraparoundModeVerticalEnabled(); + update(); +} + QVariant KisOpenGLCanvas2::inputMethodQuery(Qt::InputMethodQuery query) const { return processInputMethodQuery(query);