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

Issue #1207 - Bucket features #1630

Merged
merged 35 commits into from
Jul 13, 2021
Merged

Conversation

MrStevns
Copy link
Member

@MrStevns MrStevns commented Apr 18, 2021

Inspired by davids work I set myself to see if I could improve on his expansion algorithm, as I found it problematic that it would be slower the more it needed to expand. I had recently bumped into an interesting image processing algorithm that could tell me the nearest pixels to a given color. This meant that by only searching the image once I could find pixels closest to the color and afterwards simply threshold based on the expand value. The result of this means the algorithm takes the same amount of time to expand a given region as we still only search the image once, compared to looping through the images x times which is not so problematic if you work with a relatively small image or only expand using 2-3 pixels but any more will take significantly longer.

PR content

I believe the PR fulfills most of the checkboxes in #1207 thus I think the issue can be closed after this PR has been merged.
What do you think?

Here's the UI when on bitmap layer
image

New feature - Fill to can be either:

  • Current layer
  • Layer below (old pencil behavior)

An example of how "Layer below" works:
fill-layer-below

New feature - Reference can be either:

  • Current layer
  • All layers

An example of how "all layers" reference mode works:
fill-all-layers

The all layer functionality basically flattens all bitmap layers of the current keyframe before it is run through by our floodfill algorithm, thus we can use all layers as reference to fill and in the end decide where we want to fill to.

Updated feature - Color tolerance:

  • Can be toggled on/off

New feature - Expand fill:

  • Can be toggled on/off
  • Easily changed using the slider

New feature - Drag to fill
fill-drag2

From a technical perspective I have moved all bucket related tool property functionality to its own widget.
The bitmap bucket logic has been moved to a separate BitmapBucket class so it doesn't clutter the tool.

Pre-compiled builds are available to test here:
https://drive.google.com/drive/folders/0B7zQuPZEO_64NlFsM0JTTmwzdlk

closes #1207

MrStevns added 12 commits April 17, 2021 15:11
- Uses manhattan distance algorithm to find pixels in neighborhood and then simply expands based on that data. It's very fast. O(n^2)
- Move bucket related code to own widget
- Implements fill expand with
  - enable state
  - slider
  - spinbox
- Improves tolerance by adding same checkstate to easily enable/disable the functionality.
- Implements UI and functionality to fill to current or layer below.
- Implement UI and functionality to use current layer as reference or use all layers as reference!
@davidlamhauge
Copy link
Contributor

I've tested this feature thoroughly and finds it working as expected. There were some minor issues, that CF has taken care of.
This feature will certainly improve the bucket-fill quality of Pencil2D, and I look forward to see someone review and merge it.

This won't do as we don't backup layer movement or anything. If you create three drawings on layer 1, then go to layer two, swap the layers and undo.. the logic will (based on the index) try to undo on the wrong layer...

I've made just enough changes to make the undo/redo feature work for filling on a different layer. There might be cases i've missed but I don't want to put too much energy into this because of the undo/redo rewrite...
@Jose-Moreno
Copy link
Member

FYI, i'm testing this. So far these are the issues I've found that I'll need to share a video to showcase the issues accurately so please hold off merging this before that. I'll provide the video before the weekend.

@Jose-Moreno
Copy link
Member

@candyface Alright here's the video I promised. I was perhaps too enthusiastic so apologies if I fumble some words.

https://youtu.be/qdeTicqTN6E

I'll summarize the main issues:

  • [Bug] Clicking on a color area is not replacing the pixels when using new colors, probably due to a compositing mode mismatch. This is evidenced after noticing how the expansion keeps working when filling a layer without lineart.
  • [UX] Fill to: layer below option only solves a particular problem where each lineart layer will have a color layer pair. To solve other problem patterns we need at least a "custom layer" target option.
  • [UX] Reference: All layers option doesn't take into account layer visibility so it undermines the feature usefulness when handling many lineart levels.
  • [Feature Request]: Implement as a future PR the capability to fill pixels on top (front) or below (back) current layer existing pixels. Right now as per the mentioned fill bug, it seems the back fill is working, so having the ability to select between back/front fill would be ideal.
  • [Bug] Windows Ink is messing up with the application of the color while using a stylus. It seems to duplicate the tablet event. Mouse events work properly, and shutting down windows ink via the tablet drivers options also work properly. There seems to be a problem with Qt 5.15 at least, since other PR's i've tested (i.e David's Camera Interpolation) had a problem with right clicking through the timeline cursor.

@MrStevns MrStevns force-pushed the iss-1207-bucket-features branch from 24637bf to 8893310 Compare April 25, 2021 07:51
- Fixed when Reference was current layer and fillTo was current layer, it would not fill within correct boundaries.
- Fixed when reference was current layer and fillTo was current layer
- Fixed various cases where dragging and filling would spill
Which of course doesn't exist at this point...
- Added check to always allow filling once
- Simplify shouldFill logic
  - Fixes a few cases where filling with transparent with overlay would keep filling.. the new mAppliedColor should prevent that from happening.
- Fixed painting occurring outside region of interest
- Fixed calling state even though floodfill didn't happen
- Added test to make sure spilling doesn't occur.
Would happen when painting, then undo the steps and then fill again.. since the layer cache is expected to be valid in 90% cases, which doesn't include undo/redo.
@MrStevns
Copy link
Member Author

MrStevns commented May 3, 2021

PR is ready to be reviewed

if (tool->type() == BUCKET) {
disableAllOptions();
if (mBucketOptionsWidget == nullptr) {
mBucketOptionsWidget = new BucketOptionsWidget(editor(), this);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I prefer to create the widget at the initial stage. Especially when the widget will not be destroyed until the application ends.

}

if (mTargetFillToLayer == nullptr) {
Q_ASSERT(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean Q_ASSERT(false)? it's the case that should never happen, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And looks like the assert shouldn't be put here as the mTargetFillToLayer could be null.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming based off of the changes you made that you realize that it can't be null. editor->layers()->currentLayer() asserts the return is not null, so initialLayer can't be null. findBitmapLayerBelow will return initialLayer or another layer which is allso asserted not to be null.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I finally got it :p

}

if (mTargetFillToLayer == nullptr) {
Q_ASSERT(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as the above comment.

int bucketFillExpand = 0;
bool bucketFillExpandEnabled = 0;
int bucketFillToLayerMode = 0;
int bucketFillReferenceMode = 0;
Copy link
Member

@chchwy chchwy Jul 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like all these newly added modes are exclusively for the bucket tool. I wonder if these variables could be moved to somewhere else better? And use enum instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup I considered that too, maybe we should do that. I simply added it there since we have most of the tool properties in basetool.

@chchwy
Copy link
Member

chchwy commented Jul 13, 2021

I think this PR is working as expected and generally good to go. I encountered once a bug that bucketFillToLayerMode value doesn't match the dropdown list item but I can't replicate it consistently. probably it only happens in a special scenario.

Before I merge this PR, I will make some minor code changes mentioned above. won't touch the main part.

Great effort, well done.

@chchwy chchwy added this to the 0.7.0 milestone Jul 13, 2021
@MrStevns
Copy link
Member Author

Sounds good, thanks for reviewing :) - I generally agree with your comments. I am away from the computer for the day, so apply the changes you see fit 👍

@chchwy chchwy merged commit 0913fd6 into pencil2d:master Jul 13, 2021
@MrStevns MrStevns modified the milestones: 0.7.0, v0.6.7 Jul 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging this pull request may close these issues.

Bucket Fill Tool Behavior Regression / Re-Implementation
6 participants