Initialize the X/Y tilt fields of improted/pasted path control
points to 0, instead of 0.5, which is the normal value for these
fields in paths. This avoids calculating bogus distances when
trying to pick the path, causing picking to fail.
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).
I had this funny behavior when I was quitting GIMP with the active tool
using modifiers (for instance bucket fill). Each time I'd quit with
ctrl-q (and if the image is not dirty), the options would use the value
from the modifier state and be saved as-is. Hence at next restart, the
default value was always different!
For this, I needed distmap of the closed version of the line art (after
splines and segments are created). This will result in invisible stroke
borders added when flooding in the end. These invisible borders will
have a thickness of 0.0, which means that flooding will stop at once
after these single pixels are filled, which makes it quick, and is
perfect since created splines and segments are 1-pixel thick anyway.
Only downside is having to run "gegl:distance-transform" a second time,
but this still stays fast.
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.
This commit is based on GEGL master as I just made the auxiliary buffer
of gegl:watershed-transform optional for basic cases.
It doesn't necessarily makes the whole operation that much faster
according to my tests, but it makes the code simpler as creating this
priority map was quite unnecessary.
Pass the GEGL tile-cache size, swap path, and thread-count to plug-
ins as part of their config, and have libgimp set the plug-in's
GeglConfig accordingly upon initialization.
... 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.
When an async that was created through
gimp_parallel_run_async[_full](), and whose execution is still
pending, is being waited-upon, maximize its priority so that it
gets executed before all other pending asyncs.
Note that we deliberately don't simply execute the async in the
calling thread in this case, to allow timed-waits to fail (which is
especially important for gimp_wait()).
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.
Move swap/cache and temporary files out the GIMP user config dir:
libgimpbase: add gimp_cache_directory() and gimp_temp_directory()
which return the new default values inside XDG_CACHE_HOME and the
system temp directory. Like all directories from gimpenv.[ch] the
values can be overridden by environment variables. Improve API docs
for all functions returning directories.
Add new config file substitutions ${gimp_cache_dir} and
${gimp_temp_dir}.
Document all the new stuff in the gimp and gimprc manpages.
app: default "swap-path" and "temp-path" to the new config file
substitutions. On startup and config changes, make sure that the swap
and temp directories actually exist.
In the preferences dialog, add reset buttons to all file path pages.
Previous algorithm was relying on strokes of small radius to detect
points of interest. In order to work with various sizes of strokes, we
were computing an approximate median stroke thickness, then using this
median value to erode the binary line art.
Unfortunately this was not working that well for very fat strokes, and
also it was potentially opening holes in the line art. These holes were
usually filled back later during the spline and segment creations. Yet
it could not be totally assured, and we had some experience where color
filling would leak out of line art zones without any holes from the
start (which is the opposite of where this new feature is supposed to
go)!
This updated code computes instead some radius estimate for every border
point of strokes, and the detection of end points uses this information
of local thickness. Using local approximation is obviously much more
accurate than a single thickness approximation for the whole drawing,
while not making the processing slower (in particular since we got rid
of the quite expensive erosion step).
This fixes the aforementionned issues (i.e. work better with fat strokes
and do not create invisible holes in closed lines), and also is not
subject to the problem of mistakenly increasing median radius when you
color fill in sample merge mode (i.e. using also the color data in the
input)!
Also it is algorithmically less intensive, which is obviously very good.
This new version of the algorithm is a reimplementation in GIMP of new
code by Sébastien Fourey and David Tschumperlé, as a result of our many
discussions and tests with the previous algorithm.
Note that we had various tests, experiments and propositions to try and
improve these issues. Skeletonization was evoked, but would have been
most likely much slower. Simpler erosion based solely on local radius
was also a possibility but it may have created too much noise (skeleton
barbs), with high curvature, hence may have created too many new
artificial endpoints.
This new version also creates more endpoints though (and does not seem
to lose any previously detected endpoints), which may be a bit annoying
yet acceptable with the new bucket fill stroking interaction. In any
case, on simple examples, it seems to do the job quite well.
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.
The parallel_distribute() family of functions has been migrated to
GEGL. Remove the gimp_parallel_distribute() functions from
gimp-parallel, and replace all uses of these functions with the
corresponding gegl_parallel_distrubte() functions.
Nothing said what was going to be the order of the page, except by
testing. Now there will be an explicit text, which will be automatically
updated when checking the "reverse order" box.
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.
The older labelling based off CImg code was broken (probably because of
me, from my port). Anyway I realized what it was trying to do was too
generic, which is why we had to fix the result later (labeling all
non-stroke pixels as 0, etc.). Instead I just implemented a simpler
labelling and only look for stroke regions. It still over-label a bit
the painting but a lot less, and is much faster.
I don't actually need to loop through borders first. This is what the
abyss policy is for, and I can simply check the iterator position to
verify I am within buffer boundaries or not.
This simplifies the code a lot.
We actually don't need to compute distance map. I just make the simplest
priority map, with 1 any line art pixel and 0 any other pixel (in mask
or not), lowest priority being propagated first.
And let the flooding begin!
... labels buffer.
We can watershed directly the mask buffer being correctly flagged.
This commit relies on merge request gegl!8 being accepted and merged.