In the warp tool, set the drawable-filter's crop area to the
combined stroke bounds, so that, when comitting the tool, only this
area is processed, instead of the entire drawable area.
In GimpFilterTool, show the region combo when applying a non-point
op, as well as when applying a position-dependent point op. The
result of non-point ops may depend on the choice of input region,
even if the op is not position-dependent.
... in GimpBucketFillOptions for the line art algorithm.
Inside GimpLineArt, there are still 2 properties, but we don't show them
anymore in the Bucket Fill tool options. One of the main reason is
probably that it's hard to differentiate their usage. One is to close
with curved lines, the other with straight segments. Yet we don't
actually have any control on one or the other. All one knows is that you
can have "holes" in your drawing of a given size and you want them
close-like for filling. Only reason I can see to have 2 types of closure
is whether you'd want to totally disable one type of closure (then you
set it to 0). But this is a very limited reason for making the options
less understandable overall, IMO.
So for the time being, let's show up only a single option which sets
both properties in GimpLineArt. As patdavid says "it makes sense as a
first pass".
Also rename the option to shorter/simpler "Maximum gap length". Thanks
to patdavid and pippin for helping on figuring out this better label!
Finally I am bumping the default for the gaps to 100px. The original
values were ok for the basic small images used in demos, but not for
real life image where it was always too short (even 100px may still be
too short actually, but much better than the 20 and 60px from before!).
Practically it means that the algorithm won't close line art anymore
with both settings at 0. This can nevertheless still be a very useful
tool when you have a drawing style with well-closed lines. In such a
case, you will still profit from the color flooding under the line art
part of the algorithm.
Moreover with such well-closed zones from start, you don't get the
over-segmentation anymore and the threaded processing will be faster
obviously.
In the warp tool, when the warp is empty and the current behavior
has no effect as a result (i.e., when it's ERASE or SMOOTH), show
an error message in the status bar, and blink the behavior combo
widget in the tool options, to hint at the source of the error.
In the warp tool, when no stroke events are selected, blink the
stroke frame widget in the tool options, in addition to showing an
error message in the status bar, to hint at the source of the
error.
The enumerators of the GimpWarpBehavior enum, except for MOVE, had
a GEGL_ prefix, rather than a GIMP_ prefix, for some reason.
Change all of them to GIMP_.
This was my initial choice, but the more I think about it, the less I am
sure this was the right choice. There was some common code (as I was
making a common composite bucket fill once the line art was generated),
but there is also a lot of different code and the functions were filled
of exception when we were doing a line art fill. Also though there is a
bit of color works (the way we decide whether a pixel is part of a
stroke or not, though currently this is basic grayscale threshold), this
is really not the same as other criterions. In particular this was made
obvious on the Select by Color tool where the line art criterion was
completely meaningless and would have had to be opted-out!
This commit split a bit the code. Instead of finding the line art in the
criterion list, I add a third choice to the "Fill whole selection"/"Fill
similar colors" radio. In turn I create a new GimpBucketFillArea type
with the 3 choices, and remove line art value from GimpSelectCriterion.
I am not fully happy yet of this code, as it creates a bit of duplicate
code, and I would appreciate to move some code away from gimpdrawable-*
and gimppickable-* files. This may happen later. I break the work in
pieces to not get too messy.
Also this removes access to the smart colorization from the API, but
that's probably ok as I prefer to not freeze options too early in the
process since API needs to be stable. Probably we should get a concept
of experimental API.
Currently in bucket fill tool, the modifier was only switching fg to bg
and bg to fg, and was doing nothing when pattern was set. I make it
switch to fg as well (and remember which was the original value).
In all tools, when the current item can't be edited due to its lock
mask, use gimp_tools_blink_lock_box(), added in the previous
commit,to blink the lock box of the corresponding dockable, in
addition to showing an error message in the status bar, to hint at
the source of the error.
Add gimp_tools_blink_lock_box() utility function, in a new
gimptools-utils.c file, which takes a GimpItem, and blinks the
GimpItemTreeView lock-box of the corresponding dockable. This can
be used to hint that the item's lock toggles are preventing it from
being edited.
In the paint tools, when the current paint mode is invalid, i.e.,
when it requires an alpha channel, but the the current drawable has
no alpha channel, or its alpha channel is locked, blink the paint-
mode box widget in the tool options, in addition to showing an
error message in the status bar, to hint at the source of the
error.
In the transform tools, when there is no item of the selected type
to transform, blink the move-type box widget in the tool options,
in addition to showing an error message in the status bar, to hint
at the source of the error.
In the move tool, when there is no item of the selected type to
move, blink the move-type box widget in the tool options, in
addition to showing an error message in the status bar, to hint at
the source of the error.
In the selection tools, when the selected operation is invalid,
i.e., when trying to subtract-from or intersect-with an empty
selection, blink the selection-mode box widget in the tool options,
in addition to showing an error message in the status bar, to hint
at the source of the error.
The code was too much spread out, in core and tool code, and also it was
made too specific to fill. I'll want to reuse this code at least in the
fuzzy select tool. This will avoid code duplication, and also make this
new process more self-contained and simpler to review later (the
algorithm also has a lot of settings and it is much cleaner to have them
as properties rather than passing these as parameters through many
functions).
The refactoring may not be finished; that's at least a first step.
Add a boolean "direct" parameter to gimp_projection_flush_now(),
which specifies if the projection buffer should only be invalidated
(FALSE), or rendered directly (TRUE).
Pass TRUE when flushing the projection during painting, so that the
affected regions are rendered in a single step, instead of tile-by-
tile. We previously only invalidated the projection buffer, but
since we synchronously flush the display right after that, the
invalidated regions would still get rendered, albeit less
efficiently.
Likewise, pass TRUE when benchmarking the projection through the
debug action, and avoid flushing the display, to more accurately
measure the render time.
In particular, it allows to easily color pick. This just makes sense as
the bucket fill is definitely what one could call a "color tool", and
being able to easily change color without having to constantly switch to
color picker tool nor open a color chooser dialog is a must.
The fill type option (FG/BG/Pattern) was already mapped to the common
toggle behavior key (Ctrl on Linux), which is commonly used for
switching to color picker on paint tools. So I decided to remap the fill
type switch to GDK_MOD1_MASK (Alt on Linux) to keep consistent with
other tools (at the price of a change for anyone used to this modifier,
though I doubt it was that much used).
I also made possible to combine the 2 modifiers (so you could pick the
foreground or background color with ctrl and ctrl-alt).
The distance map has all the information we need already. Also we will
actually grow up to the max radius pixel (middle pixel of a stroke).
After discussing with Aryeom, we realized it was better to fill a stroke
fully (for cases of overflowing, I already added the "Maximum growing
size" property anyway).
When an error occurs, we want to prevent overwriting any previous
version of the file by incomplete contents. So run
g_output_stream_close() with a cancelled GCancellable to do so.
See also discussion in #2565.
When flooding the line art, we may overflood it in sample merge (which
would use color in the line art computation). And if having all colors
on the same layer, this would go over other colors (making the wrong
impression that the line art leaked).
This new option is mostly to keep some control over the mask growth.
Usually a few pixels is enough for most styles of drawing (though we
could technically allow for very wide strokes).
We don't really need to flow every line art pixel and this new
implementation is simpler (because we don't actually need over-featured
watershedding), and a lot lot faster, making the line art bucket fill
now very reactive.
For this, I am keeping the computed distance map, as well as local
thickness map around to be used when flooding the line art pixels
(basically I try to flood half the stroke thickness).
Note that there are still some issues with this new implementation as it
doesn't properly flood yet created (i.e. invisible) splines and
segments, and in particular the ones between 2 colored sections. I am
going to fix this next.
Introduced in commit b4e12fbbbb:
gimp_pickable_contiguous_region_prepare_line_art_async() was running
gimp_pickable_flush(), which provokes the "rendered" signal on the
image projection when a change occured. As a result, it was calling
gimp_bucket_fill_compute_line_art() within itself and since
tool->priv->async was not set yet, none of the call were canceled. Hence
the same line art is computed twice, but one is leaked.
Make sure we block this signal handler as a solution.
... and use in bucket-fill tool
Add gimp_pickable_contiguous_region_prepare_line_art_async(), which
computes a line-art asynchronously, and use it in the bucket-fill
tool, instead of having the tool create the async op.
This allows the async to keep running even after the pickable dies,
since we only need the pickable's buffer, and not the pickable
itself. Previously, we reffed the pickable for the duration of the
async, but we could still segfault when unreffing it, if the
pickable was a drawable, and its parent image had already died.
Furthermore, let the async work on a copy of the pickable's buffer,
rather than the pickable's buffer directly. This avoids some race
conditions when the pickable is the image (i.e., when "sample
merged" is active), since then we're using image projection's
buffer, which is generally unsafe to use in different threads
concurrently.
Also, s/! has_alpha/has_alpha/ when looking for transparent pixels,
and quit early, at least during this stage, if the async in
canceled.
When computing line-art, don't ref the bucket-fill tool in the
async data, and rather cancel any ongoing async upon tool
destruction, so that the async callback doesn't attept to touch the
now-dead tool. This avoids segfaulting in the async callback when
switching to a different tool, while a line-art async operation is
active.
Additionally, always cancel any previous async operation in
gimp_bucket_fill_compute_line_art(), even if not starting a new
one.
In the line-art async function, pass ownership over the resulting
buffer to the async object, so that the buffer is properly freed in
case the async in canceled after line-art computation is complete,
but before the completion callback is called.
Also, clear the tool's async pointer in the completion callback, to
avoid leaking the last issued async.
The "update" signal on drawable or projection can actually be emitted
many times for a single painting event. Just add new signals ("painted"
on GimpDrawable and "rendered" on GimpProjection) which are emitted once
for a single update (from user point of view), at the end, after actual
rendering is done (i.e. after the various "update" signals).
Also better support the sample merge vs current drawable paths for
bucket fill.
Since commit b00037b850, erosion size is not used anymore, as this step
has been removed, and the end point detection now uses local thickness
of strokes instead.
Other bucket fills are now done as filter until committed, but basic
selection fill is still done automatically. So let's make sure the
canvas is updated immediately (as it used to be before my changes).
In the scale tool, when the "around center" option is toggled,
scale the item around its center not only through canvas
interaction, but also when entering width/height values through the
tool GUI.
I have not added all the options for this new tool yet, but this sets
the base. I also added a bit of TODO for several places where we need to
make it settable, in particular the fuzzy select tool, but also simply
PDB calls (this will need to be a PDB context settings.
Maybe also I will want to make some LineArtOptions struct in order not
to have infinite list of parameters to functions. And at some point, it
may also be worth splitting a bit process with other type of
selection/fill (since they barely share any settings anyway).
Finally I take the opportunity to document a little more the parameters
to gimp_lineart_close(), which can still be improved later (I should
have documented these straight away when I re-implemented this all from
G'Mic code, as I am a bit fuzzy on some details now and will need to
re-understand code).
Rather than just having a click interaction, let's allow to "paint" with
the bucket fill. This is very useful for the new "line art" colorization
since it tends to over-segment the drawing. Therefore being able to
stroke through the canvas (rather than click, up, move, click, etc.)
makes the process much simpler. This is also faster since we don't have
to recompute the line art while a filling is in-progress.
Note that this new behavior is not only for the line art mode, but also
any other fill criterion, for which it can also be useful.
Last change of behavior as a side effect: it is possible to cancel the
tool changes the usual GIMP way (for instance by right clicking when
releasing the mouse button).
Right now, this is mostly meaningless as it is still done sequentially.
But I am mostly preparing the field to pre-compute the line art as
background thread.
Replace GimpTransformTool's 'drawable' field with an 'item' field,
and have GimpTransformGridTool set it to the active item, to which
the transformation is applied, during its initialization. In
gimp_transform_tool_get_active_item(), return the value of the
transform tool's 'item' field, if not NULL, instead of the image's
active item. This makes sure we apply that transform-grid tools
apply the transformation for the item for which they were
activated, even if the image's active item has changed.
In the warp tool, don't commit a trivial (empty) transform. This
is especially important now that exiting the tool through undo
causes it to get comitted (... with a trivial transform).
... changing layers and warping layer B
Add a new GimpToolControl::dirty_action field, which specifies the
tool action to perform when the a dirty event matching the tool
control's dirty mask occurs; this field defaults to HALT. Apply
this action to the active tool in tool-manager in response to a
matching dirty event, instead of unconditionally halting the tool.
Likewise, use this action to stop the active tool in response to a
button-press event on a different drawable in the same image.
Set the dirty action of the gradient and warp tools to COMMIT, so
that they get comitted, rather than stopped, in cases such as
switching layers (including switching to/from quick-mask mode),
and, for the warp tool, changing the selection.
... the XCF file
Add a "saving" signal to GimpImage, which is emitted when the image
is about to be saved or exported (but before it's actually saved/
exported). Connect to this signal in tool-manager, and commit the
current tool in response (unless its GimpToolControl::preserve is
TRUE).
We currently construct the tool-options GUI for all the tools at
startup, which takes a significant amount of time. Instead,
only register the GUI construction function with the tool-options
object, using the new gimp_tools_set_tool_options_gui_func()
function, and use the registered function to construct the GUI when
actually needed.
... after erasing all points
When erasing the last remaining point in the iscissors tool, halt
the tool, rather than leaving the tool active with an empty curve,
which it is not prepared to handle, and which results in a segfault
once trying to add a new point.
Additionally, when erasing the last remaining segment (i.e., the
two last remaining points), don't erase the entire segment (i.e.,
both points), but rather convert the segment to its initial point,
so that, in effect, we only erase the last point of the segment.