Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New canvas cursor and quick cursor implementation #1806

Merged
merged 25 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
31ce667
Implement width cursor
MrStevns Nov 25, 2023
8d2de73
Make feather cursor adjust properly
MrStevns Dec 3, 2023
dc394cb
Invert feather values
MrStevns Dec 16, 2023
ca6f87e
Fix circle rendering becoming near invisible when width and feather a…
MrStevns Dec 17, 2023
4cec4b4
Cleanup cursor logic in strokeTool
MrStevns Dec 17, 2023
1779072
Remove tolerance quick cursor shortcut
MrStevns Jan 9, 2024
cb53144
Don't show feather circle when it's fixed size
MrStevns Jan 9, 2024
ebe30ee
Simplify cursor paint methods
MrStevns Jan 9, 2024
d533328
Merge branch 'master' into new-canvas-cursor-impl
MrStevns Jan 9, 2024
5c69f33
Fix CI Qt not compiling
MrStevns Jan 9, 2024
6c1cac3
Add missing license to CanvasCursorPainter
MrStevns Jan 9, 2024
24df0e6
Review changes
J5lx Feb 25, 2024
96151d7
Fix build failure on old MSVC versions
J5lx Feb 25, 2024
3ba49df
Review change: move quick cursor logic into stroke tool
MrStevns Feb 28, 2024
e4b6a01
Remove redundant neutral multiplication
MrStevns Feb 28, 2024
f139cf1
Fix cursor lingers on canvas
MrStevns Mar 2, 2024
5e09c2a
Migrate canvas cursor setting from dotted to canvas cursor
MrStevns Mar 2, 2024
138700a
Fix cursor would disappear if outside main window
MrStevns Mar 2, 2024
f0eca1e
Fix view updates would send signals to all stroke tools
MrStevns Mar 2, 2024
20ea7c6
Remove useFeather
MrStevns Mar 2, 2024
a070438
Keep internal config file key for canvas cursor setting
J5lx Mar 5, 2024
efbf77e
Fix canvas cursor artifacts when leaving ScribbleArea
J5lx Mar 5, 2024
f3c2754
Apply suggestion to return either current tool event or widget event
MrStevns Mar 6, 2024
31e64d9
Fix tool event handling
J5lx Mar 6, 2024
6a1405b
Bucket tool: remove quick sizing
MrStevns Mar 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions app/src/generalpage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ GeneralPage::GeneralPage() : ui(new Ui::GeneralPage)
connect(ui->antialiasingBox, &QCheckBox::stateChanged, this, &GeneralPage::antiAliasCheckboxStateChanged);
connect(ui->curveSmoothingLevel, &QSlider::valueChanged, this, &GeneralPage::curveSmoothingChanged);
connect(ui->highResBox, &QCheckBox::stateChanged, this, &GeneralPage::highResCheckboxStateChanged);
connect(ui->dottedCursorBox, &QCheckBox::stateChanged, this, &GeneralPage::dottedCursorCheckboxStateChanged);
connect(ui->canvasCursorBox, &QCheckBox::stateChanged, this, &GeneralPage::canvasCursorCheckboxStateChanged);
connect(ui->gridSizeInputW, spinValueChanged, this, &GeneralPage::gridWidthChanged);
connect(ui->gridSizeInputH, spinValueChanged, this, &GeneralPage::gridHeightChanged);
connect(ui->actionSafeCheckBox, &QCheckBox::stateChanged, this, &GeneralPage::actionSafeCheckBoxStateChanged);
Expand Down Expand Up @@ -141,8 +141,8 @@ void GeneralPage::updateValues()
ui->toolCursorsBox->setChecked(mManager->isOn(SETTING::TOOL_CURSOR));
QSignalBlocker b5(ui->antialiasingBox);
ui->antialiasingBox->setChecked(mManager->isOn(SETTING::ANTIALIAS));
QSignalBlocker b6(ui->dottedCursorBox);
ui->dottedCursorBox->setChecked(mManager->isOn(SETTING::DOTTED_CURSOR));
QSignalBlocker b6(ui->canvasCursorBox);
ui->canvasCursorBox->setChecked(mManager->isOn(SETTING::CANVAS_CURSOR));
QSignalBlocker b7(ui->gridSizeInputW);
ui->gridSizeInputW->setValue(mManager->getInt(SETTING::GRID_SIZE_W));
QSignalBlocker b11(ui->gridSizeInputH);
Expand Down Expand Up @@ -233,9 +233,9 @@ void GeneralPage::toolCursorsCheckboxStateChanged(int b)
mManager->set(SETTING::TOOL_CURSOR, b != Qt::Unchecked);
}

void GeneralPage::dottedCursorCheckboxStateChanged(int b)
void GeneralPage::canvasCursorCheckboxStateChanged(int b)
{
mManager->set(SETTING::DOTTED_CURSOR, b != Qt::Unchecked);
mManager->set(SETTING::CANVAS_CURSOR, b != Qt::Unchecked);
}

void GeneralPage::gridWidthChanged(int value)
Expand Down
2 changes: 1 addition & 1 deletion app/src/generalpage.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private slots:
void shadowsCheckboxStateChanged(int b);
void antiAliasCheckboxStateChanged(int b);
void toolCursorsCheckboxStateChanged(int b);
void dottedCursorCheckboxStateChanged(int b);
void canvasCursorCheckboxStateChanged(int b);
void highResCheckboxStateChanged(int b);
void gridCheckBoxStateChanged(int b);
void curveSmoothingChanged(int value);
Expand Down
1 change: 0 additions & 1 deletion app/src/mainwindow2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -999,7 +999,6 @@ void MainWindow2::preferences()
{
clearKeyboardShortcuts();
setupKeyboardShortcuts();
ui->scribbleArea->updateCanvasCursor();
mPrefDialog = nullptr;
});

Expand Down
5 changes: 3 additions & 2 deletions app/src/tooloptionwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ GNU General Public License for more details.
#include "util.h"
#include "layer.h"
#include "layermanager.h"
#include "stroketool.h"
#include "toolmanager.h"

ToolOptionWidget::ToolOptionWidget(QWidget* parent) : BaseDockWidget(parent)
Expand All @@ -53,11 +54,11 @@ void ToolOptionWidget::initUI()

QSettings settings(PENCIL2D, PENCIL2D);

ui->sizeSlider->init(tr("Width"), SpinSlider::EXPONENT, SpinSlider::INTEGER, 1, 200);
ui->sizeSlider->init(tr("Width"), SpinSlider::EXPONENT, SpinSlider::INTEGER, StrokeTool::WIDTH_MIN, StrokeTool::WIDTH_MAX);
ui->sizeSlider->setValue(settings.value("brushWidth", "3").toDouble());
ui->brushSpinBox->setValue(settings.value("brushWidth", "3").toDouble());

ui->featherSlider->init(tr("Feather"), SpinSlider::LOG, SpinSlider::INTEGER, 1, 99);
ui->featherSlider->init(tr("Feather"), SpinSlider::LOG, SpinSlider::INTEGER, StrokeTool::FEATHER_MIN, StrokeTool::FEATHER_MAX);
ui->featherSlider->setValue(settings.value("brushFeather", "5").toDouble());
ui->featherSpinBox->setValue(settings.value("brushFeather", "5").toDouble());
}
Expand Down
6 changes: 3 additions & 3 deletions app/ui/generalpage.ui
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="dottedCursorBox">
<widget class="QCheckBox" name="canvasCursorBox">
<property name="text">
<string>Dotted Cursor</string>
<string>Canvas Cursor</string>
</property>
</widget>
</item>
Expand Down Expand Up @@ -515,7 +515,7 @@
<tabstop>windowOpacityLevel</tabstop>
<tabstop>shadowsBox</tabstop>
<tabstop>toolCursorsBox</tabstop>
<tabstop>dottedCursorBox</tabstop>
<tabstop>canvasCursorBox</tabstop>
<tabstop>checkerBackgroundButton</tabstop>
<tabstop>whiteBackgroundButton</tabstop>
<tabstop>greyBackgroundButton</tabstop>
Expand Down
2 changes: 2 additions & 0 deletions core_lib/core_lib.pro
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ INCLUDEPATH += src \
PRECOMPILED_HEADER = src/corelib-pch.h

HEADERS += \
src/canvascursorpainter.h \
src/corelib-pch.h \
src/graphics/bitmap/bitmapbucket.h \
src/graphics/bitmap/bitmapimage.h \
Expand Down Expand Up @@ -115,6 +116,7 @@ HEADERS += \


SOURCES += src/graphics/bitmap/bitmapimage.cpp \
src/canvascursorpainter.cpp \
src/graphics/bitmap/bitmapbucket.cpp \
src/graphics/bitmap/tile.cpp \
src/graphics/bitmap/tiledbuffer.cpp \
Expand Down
97 changes: 97 additions & 0 deletions core_lib/src/canvascursorpainter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*

Pencil2D - Traditional Animation Software
Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
Copyright (C) 2012-2020 Matthew Chiawen Chang

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

*/
#include "canvascursorpainter.h"

#include <QPainter>

CanvasCursorPainter::CanvasCursorPainter()
{
setupPen();
}

void CanvasCursorPainter::setupPen()
{
mCursorPen = QPen(Qt::gray);
mCursorPen.setWidthF(1);
mCursorPen.setCosmetic(true);
}

void CanvasCursorPainter::paint(QPainter& painter, const QRect& blitRect)
{
if (mOptions.isAdjusting || mOptions.showCursor) {
if (mOptions.useFeather) {
paintFeatherCursor(painter, blitRect, mOptions.widthRect, mOptions.featherRect);
}
paintWidthCursor(painter, blitRect, mOptions.widthRect);
mIsDirty = true;
}
}

void CanvasCursorPainter::preparePainter(const CanvasCursorPainterOptions& painterOptions, const QTransform& viewTransform)
{
mOptions = painterOptions;
if (mOptions.isAdjusting || mOptions.showCursor) {
mOptions.widthRect = viewTransform.mapRect(mOptions.widthRect);
mOptions.featherRect = viewTransform.mapRect(mOptions.featherRect);
}
}

void CanvasCursorPainter::paintFeatherCursor(QPainter& painter, const QRect& blitRect, const QRectF& widthCircleBounds, const QRectF& featherCircleBounds)
{
// When the circles are too close to each other, the rendering will appear dotted or almost
// invisible at certain zoom levels.
if (widthCircleBounds.width() - featherCircleBounds.width() <= 1) {
return;
}

painter.save();

painter.setClipRect(blitRect);
painter.setPen(mCursorPen);
painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
painter.drawEllipse(featherCircleBounds);

painter.restore();
}

void CanvasCursorPainter::paintWidthCursor(QPainter& painter, const QRect& blitRect, const QRectF& widthCircleBounds)
{
painter.save();

painter.setClipRect(blitRect);
painter.setPen(mCursorPen);

painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);

// Only draw the cross when the width is bigger than the cross itself
if (widthCircleBounds.width() > 8) {
const QPointF& pos = widthCircleBounds.center();
painter.drawLine(QPointF(pos.x() - 2, pos.y()), QPointF(pos.x() + 2, pos.y()));
painter.drawLine(QPointF(pos.x(), pos.y() - 2), QPointF(pos.x(), pos.y() + 2));
}

painter.drawEllipse(widthCircleBounds);
painter.restore();

mDirtyRect = widthCircleBounds.toAlignedRect();
}

void CanvasCursorPainter::clearDirty()
{
mDirtyRect = QRect();
mIsDirty = false;
}
61 changes: 61 additions & 0 deletions core_lib/src/canvascursorpainter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*

Pencil2D - Traditional Animation Software
Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
Copyright (C) 2012-2020 Matthew Chiawen Chang

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

*/
#ifndef CANVASCURSORPAINTER_H
#define CANVASCURSORPAINTER_H

#include <QPen>

class QPainter;

struct CanvasCursorPainterOptions
{
QRectF widthRect;
QRectF featherRect;
bool isAdjusting;
bool useFeather = false;
bool showCursor = false;
};

class CanvasCursorPainter
{

public:
CanvasCursorPainter();
void paint(QPainter& painter, const QRect& blitRect);

void preparePainter(const CanvasCursorPainterOptions& painterOptions, const QTransform& viewTransform);

const QRect dirtyRect() { return mDirtyRect; }
bool isDirty() const { return mIsDirty; }
void clearDirty();

private:

void setupPen();

/// @brief precision circular cursor: used for drawing a cursor on the canvas.
void paintWidthCursor(QPainter& painter, const QRect& blitRect, const QRectF& widthCircleBounds);
void paintFeatherCursor(QPainter& painter, const QRect& blitRect, const QRectF& widthCircleBounds, const QRectF& featherCircleBounds);

CanvasCursorPainterOptions mOptions;
QRect mDirtyRect;
bool mIsDirty = false;

QPen mCursorPen;
};

#endif // CANVASCURSORPAINTER_H
16 changes: 7 additions & 9 deletions core_lib/src/graphics/bitmap/tiledbuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,15 @@ Tile* TiledBuffer::getTileFromIndex(const TileIndex& tileIndex)
return selectedTile;
}

void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, int brushCursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) {
void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) {
const QRectF brushRect(point.x() - 0.5 * brushWidth, point.y() - 0.5 * brushWidth, brushWidth, brushWidth);
const float tileSize = UNIFORM_TILE_SIZE;
const int width = qMax(brushCursorWidth,brushWidth);

// Gather the number of tiles that fits the size of the brush width
const int xLeft = qFloor((qFloor(point.x() - width)) / tileSize);
const int xRight = qFloor((qFloor(point.x() + width)) / tileSize);
const int yTop = qFloor(qFloor(point.y() - width) / tileSize);
const int yBottom = qFloor(qFloor(point.y() + width) / tileSize);
const int xLeft = qFloor((qFloor(point.x() - brushWidth)) / tileSize);
const int xRight = qFloor((qFloor(point.x() + brushWidth)) / tileSize);
const int yTop = qFloor(qFloor(point.y() - brushWidth) / tileSize);
const int yBottom = qFloor(qFloor(point.y() + brushWidth) / tileSize);

for (int tileY = yTop; tileY <= yBottom; tileY++) {
for (int tileX = xLeft; tileX <= xRight; tileX++) {
Expand Down Expand Up @@ -108,11 +107,10 @@ void TiledBuffer::drawImage(const QImage& image, const QRect& imageBounds, QPain
}


void TiledBuffer::drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush brush,
void TiledBuffer::drawPath(QPainterPath path, QPen pen, QBrush brush,
QPainter::CompositionMode cm, bool antialiasing)
{
const int pathWidth = pen.width();
const int width = (qMax(pathWidth,cursorWidth) + 1);
const int width = pen.width();;
const float tileSize = UNIFORM_TILE_SIZE;
const QRectF pathRect = path.boundingRect();

Expand Down
4 changes: 2 additions & 2 deletions core_lib/src/graphics/bitmap/tiledbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ class TiledBuffer: public QObject
bool isValid() const { return !mTiles.isEmpty(); }

/** Draws a brush with the specified parameters to the tiled buffer */
void drawBrush(const QPointF& point, int brushWidth, int brushCursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing);
void drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing);
/** Draws a path with the specified parameters to the tiled buffer */
void drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush brush,
void drawPath(QPainterPath path, QPen pen, QBrush brush,
QPainter::CompositionMode cm, bool antialiasing);
/** Draws a image with the specified parameters to the tiled buffer */
void drawImage(const QImage& image, const QRect& imageBounds, QPainter::CompositionMode cm, bool antialiasing);
Expand Down
Loading
Loading