This allows an extension removal to be cancelable for a given session
(at end of session, the extension removal is definitive and you have to
reinstall it if you want it back).
In GimpFilterTool, properly clean up the controller's widget weak-
pointer and signal-handlers upon destruction, to avoid invalid
memory access when the widget is destroyed, if the widget outlives
the controller.
In the vairous gimp_gegl_create_foo_node() functions, set the
parent node's underlying operation node, so that
gimp_gegl_apply_cached_operation() avoids duplicating the source
buffer when applying these nodes (all underlying operations are
currently point ops.)
The GUI is uglier than ever. I added a frame in a frame to put an
uninstall button. Please don't mind it, this is temporary. I just needed
to test uninstallation and don't know yet what are the proper widgets
to use for a nice design. :P
This completes my earlier commit 406279e4ef.
Extension installation is not about just decompressing a file in the
right folder. We must also make the extension manager and the GUI aware
of this newly available extension.
Removing an extension means just unloading it and temporarily move it to
an "uninstalled" list. The actual files will only be deleted when
cleanly exiting the program, hence finalizing the extension manager.
This will allow undoing an extension removal easily.
Apparently Microsoft added just recently the feature to install user
font (as opposed to system-wide fonts), without administration rights
(yes, only now, how crazy is that?). Right now GIMP does not see fonts
there.
We have an upstream report at fontconfig where such a default search
path should happen.
See: https://gitlab.freedesktop.org/fontconfig/fontconfig/issues/144
Until it gets fixed there, let's just add the user fonts dir ourselves
in GIMP. This code should get killed later.
Notes:
- I renamed various DEFAULT_* data macros to GIMP_DEFAULT_* because
DEFAULT_PALETTE was conflicting with another macro in Windows API!
- Also I removed the DATADIR macro set under app/config/ because it is
also conflicting and anyway we use it in no files on this level.
- This is not perfectly tested on Windows. Please everyone with Windows
access, could you build and test if it works fine before release?
In GimpFreeSelectTool, flush the image when committing the tool, if
the seletion is created at the time of the commit (i.e., if the
polygon is not closed prior to the commit).
In gimp_selection_tool_start_change(), increase the priority of the
idle source used for hiding the selection so that it's run before
the canvas is redrawn, to avoid flickering the previous selection.
In GimpFreeSelectTool, use gimp_selection_tool_{start,end}_change()
to create the selection as soon as the polygon is closed, and
update it when the polygon, or the relevant tool-options, change,
similarly to GimpRectangleSelectTool.
We currently derive GimpForegroundSelectTool from
GimpFreeSelectTool, which prevents us from making changes that are
limited to the free-select tool.
Factor out the common free-select and foreground-select logic into
a new GimpPolygonSelectTool base-class, and derive both from this
class.
Move GimpRectangleSelectTool's image undo/redo logic to
GimpSelectionTool, by adding a pair of
gimp_selection_tool_{start,end}_change() functions. These
functions should be called by subclasses before/after changing the
selection, having the functions take care of undoing/redoing the
existing selection as necessary. This allows us to reuse this
logic in other selection tools, specifically, the free-select tool.
In gimp_container_tree_view_clear_items(), temporarily unset the
tree-view's model before clearing it, so that name editing is
stopped beforehand. Otherwise, name editing is stopped once the
corresponding item is removed from the store, causing us to rename
the wrong item.
In gimp_open_dialog_set_image(), use a weak pointer for storing the
current image, to avoid a segfault in file_open_dialog_response()
if the active image at the time of the open action has been closed
before confirming the dialog.
In the Curves tool, when the image precision is greater than 8-bpc,
use a 0.00-100.00 range for the point-coordinate spin-buttons,
instead of a 0-255 range.
Allow setting the type of GimpCurve control-points to either SMOOTH
or CORNER. Smooth points produce a smooth curve, while corner
points produce a sharp curve (previously, all points were smooth).
In GimpCureView, display corner points using a diamond shape,
instead of a circle.
In the Curves tool, allow changing the curve's point types.
In GimpCurve, replace the use of a fixed-length control-point array
with a dynamically-sized array. Adapt GimpCurve's interface, and
the rest of the code.
In addition to simplifying the code, this fixes a bug where the
curve object could be broken by moving the mouse too fast (yep...),
and allows more accurate point placement, both in the GUI editor,
and through canvas interaction in the Curves tool (see issue #814).
In GimpCurveView, when holding down Ctrl while adding/dragging a
point, snap the y-coordinate to the original curve (at the start of
the drag). This is particularly useful for adding points along the
curve, without changing their y-coordinate.
Likewise, have the coordinate indicator show the snapped
coordinate.
In GimpCurveView, when dragging an existing curve point, don't
immediately move the point to the cursor position uppon button
press, but rather move it relative to its current position as the
cursor moves. This allows selecting a point without moving it, and
adjusting its position more easily.
Additionally, when the cursor hovers above a point, or when
dargging a point, have the coordinate indicator show the point's
position, rather than the cursor's.
Add an "Incremental" option to the Dodge/Burn tool, which,
similarly to the Paintbrush, Pencil, and Eraser tools, applies the
effect incrementally as the pointer moves.
File extension (.gex) may still change if anything better is proposed.
This format is currently just a compressed archive containing the
extension data (plug-in, brushes or whatever) and the metadata file.
For now, opening such file will simply install it as a new extension,
keeping all file permissions and structure. Of course in the future, it
will have to trigger a confirmation dialog.
Currently the compression used is zip, which is just a first draft. This
is not a decisive choice as well. We could use some tarball compressed
in whatever other compression algorithm. I use libarchive as a new
dependency to support unarchiving as it seems to be a common library
(and since it is already used by AppStream-glib anyway, this doesn't add
any actual dependency, just make it direct).
This is useful to be able to support file formats other than image
formats. In particular I will use this in the next commit to support a
"GIMP extension" format. When GIMP will open such file, it will
install an extension (not open an image on canvas).
This is an internal flag only, i.e. only usable from core GIMP. File
formats which a plug-in can register are still only image file formats.
Only seek back to after the end of the actual brush if a following
pattern was *not* found. Got this logic wrong in the original port of
the plug-in code.
In GimpRectangleSelectTool, update the selection upon changes to
the "antialias", "feather", "feather-radius", "round-corners", and
"corner-radius" options, so that they take effect immediately,
without having to change the selection bounds.
... for "Edit/Preferences/Default Image"
In GimpTemplateEditor, don't use gimp_prop_enum_combo_box_new() for
the "Precision" combo-box, and rather synchronize the combo-box and
the template manually, since we only want to update the "Gamma"
combo-box according to the precision when it changes through the
UI, and not when the template's precision otherwise changes.
This fixes an issue where we'd always set the default gamma value
when resetting the editor's template, overwriting the template's
original gamma value.
In GimpChunkIterator, avoid preparing the current rect before
merging it back to the iterator's region, to save some work.
Additionally, strengthen the iterator's invariants and simplify
code.
Set the priority of the image window's UI-manager update idle
slightly higher than the projection idle priority, so that the
image actions are updated during projection rendering.
In GimpTransformGridTool, avoid producing non-finite coordinate
and angle values. In particular, this fixes a crash in
gimp_transform_grid_tool_get_cursor() as a result of a NaN angle
value being converted to a negative integer, hitting an assert.
In GimpViewable, don't invalidate the preview when thawed, unless
there was an explicit call to gimp_viewable_invalidate_preview()
while it was frozen. This avoids invalidating the previews of an
invisible drawable's ancestors when the drawable's preview is
frozen/thawed.
Add GimpViewable::preview_{freeze,thaw}() virtual functions, which
get called when the viewable's preview is frozen/thawed. Implement
the functions in GimpDrawable, recursively freezing the parent
drawable's preview (or the image's preview, for top-level
drawables) while the drawable's preview is frozen. For layer
masks, freeze the associated layer's parent.
This avoids updating layer-group/image previews while painting on,
or applying a filter to, a descendant layer. This both reduces
lag, and fixes a discrepancy between the layer's preview, which
isn't updated, and its parents' previews.
This was actually more of a feathering feature I added earlier, and we
already have a function for that: gimp_gegl_apply_feather(). This is
using a gaussian blur, just as what I was doing anyway. This commit also
adds the "Feather Radius" scale, similar to other tools with the
"Feather Edges". So that makes it consistent (and more useful as you can
adapt to your needs).
Include the system-wide gimp.css file, in addition to the user-
specific gimp.css file, in the generated theme.css file, instead of
copying the former into the latter when creating the user's
gimpdir. This allows us to modify the system-wide gimp.css file,
and having the changes take effect in existing installations.
In gimp_drawable_edit_fill(), when performing a non-direct fill,
use a GimpDrawableFilter with gimp:fill-source, added in the
previous commit, instead of using gimp_drawable_apply_buffer() with
an intermediate fill buffer. This avoids allocating a full-size
fill buffer, which may occupy a lot of space in pattern fills.
Add a new gimp:fill-source operation, which can act as a source
node for fill operations, instead of a fill buffer. The op takes
a GimpFillOptions object, a drawable, and a pattern offset, and
uses gimp_fill_options_create_buffer() to produce its output.
This allows performing the entire fill operation in chunks as a
graph, instead of allocating a full-size fill buffer, which can
can occupy a lot of space for pattern fills.
In gimp_drawable_fill_buffer(), when the fill-source is a pattern,
avoid going through an intermediate buffer when there's no profile
transform, and use the destination-buffer format for the
intermediate buffer, instead of the pattern format, when there is a
profile transform.
Add gimp_fill_options_get_format(), which returns the format to be
used for the fill buffer; this is the same format used during
compositing. Use this format in gimp_fill_options_create_buffer(),
instead of the drawable format.
This fixes the result of fill operations when the fill color/
pattern is not representable in the drawable format, and speeds up
color fills by avoiding color-conversion for the fill buffer during
processing.
In gimp_gegl_apply_cached_operation(), use the underlying
operation, as returned from
gimp_gegl_node_get_underlying_operation(), for testing whether the
operation is a point operation, for the purpose of avoiding
duplicating the input buffer. Likewise, avoid duplicating the
buffer when the underlying operation is a source operation.
... which allow setting/getting the "underlying operation" node of
a graph node. For example, GimpDrawableFilter constructs a complex
graph around a given operation node, which would be the underlying
operation of the graph. This allows querying the properties of the
underlying operation, given only the graph.
In recursive cases, gimp_gegl_node_get_underlying_operation()
returns the most-nested underlying operation; when no underlying
operation has been set, gimp_gegl_node_get_underlying_operation()
returns the input node.
In gimp_drawable_real_apply_buffer(), use GimpChunkIterator to blit
the applicator's output to the drawable's buffer in chunks, to
minimize the space used for intermediate results.
In GimpChunkIterator, redajust the target area at each step,
instead of at each iteration, to adapt more quickly to the current
processing speed. To avoid creating uneven chunks as a result,
only change the chunk height at the beginning of rows, unless the
resulting area would be more than twice as big as the target area.
In GimpProjection, store the priority rect in image coordinates,
and only convert it to projectable coordinates when initializing
the chunk-iterator's priority rect. This allows us to preserve the
priority rect across projectable structure/bounds changes.
In gimp_drawable_merge_filter(), don't disable the filter
applicator's output-format conversion node if the output format is
different than the drawable's format, since it may change the
result.
In gimp_drawable_merge_filter(), disable the filter applicator's
cache and output-format conversion nodes before processing the
uncached region of the filter, so that the result is written
directly to the drawable's buffer.
... immediately after an image precision change
When flushing a projection, make sure it has a buffer, instead of
bailing if it doesn't. We rely on the image projection's "update"
signal to update the display after certain operations that free the
buffer, which would previously fail to happen, and cause subsequent
flushes to be ignored until the buffer is explicitly accessed.
This fixes commit b07f810273.
In gimp_group_layer_get_size(), make sure to always set *width and
*height, even when the group is empty, so that when the function is
called through gimp_projectable_get_size() by the group's
projection, the correct size is reported. This makes sure we
update the correct area when the group becomes empty.
In gimpchannel-select, move some of the common functionality of the
various gimp_channel_select_foo() functions to gimpchannel-combine.
Furthermore, don't special-case CHANNEL_OP_INTERSECT, but rather
pass it over to gimpchannel-combine, which is now prepared to
handle it in all functions, as per the previous commits.
In gimpchannel-combine, factor out the common functionality of the
various gimp_channel_combine_foo() functions into a pair of
gimp_channel_combine_{start,end}() functions, which are called
before/after the actual gimp_gegl_mask_combine_foo() function,
respectively. In particular, these functions deal with calculating
the new channel bounds. Previously, the various
gimp_gegl_mask_combine_foo() functions would implicitly invalidate
the channel bounds (since commit
d0ae244fe8), rendering the bounds-
recalculation code ineffective. This avoids manually recalculating
the bounds in many cases, speeding up selection operations.
Improve gimp_gegl_mask_combine_ellipse_rect() -- the funciton
responsible for rendering ellipse/rounded-rectangle selections.
Most notably, this commit significantly improves the function's
performance, by identifying whole tiles, whole rows, or parts of a
row, that are fully inside, or fully outside, the ellipse, and
filling them in bulk, instead of calculating the anti-aliasing
value at each pixel, which is now only done along the
circumference.
This commit also improves anti-aliasing, by more accurately
approximating the distance from a pixel to the ellipse, and by
normalizing the distance according to the pixel's cross-section
length in the direction of the said point. In particular, we
guarantee that pixels that are fully inside/outside the ellipse
have a value of 1/0, respectively, facilitating the aforementioned
optimization.
Additionally, this commit fixes various edge cases where several
primitives coincide at a single pixel (in the rounded-rectangle
case), adds support for CHANNEL_OP_INTERSECT, and parallelizes
processing.
Don't assume that "toggled" signal means that toggle status actually
changed.
Though issue #3133 got fixed with my previous commit, let's make sure we
never create several GBinding for the same GimpChain by always checking
existence of a previous one after a "toggled" signal.
Also only create a GBinding object if one doesn't already exist.
... clicking a GimpChain.
Since commit c0c055b4e9, gimp_chain_button_set_active() emits the
"toggled" signal. There is no need to emit it separately from
GimpOperationTool when setting presets with
gimp_operation_tool_set_config().
In particular, since the "toggled" signal was even sent unconditionnally
here, our code was ending creating several GBinding for the same 2
adjustments, which was creating an infinite loop.
"binding" data can be set to NULL. Do not assume it is a proper object.
Also I was tempted to use g_object_set_data() to simply free the
GBinding object on setting a new data, but such object will also be
freed when the widget is destroyed by default. So that would also end up
in double destruction. Instead just keep current logics.
This CRITICAL was reported in #3133 but this is not the main bug.
Also note that GimpColorPanel doesn't need to react differently whether
GimpColorDialog returned OK or CANCEL, as the dialog keep track and
return the appropriate color to set in the end of the process.
... not found.
So it turns out I cannot just gtk_action_group_get_action("colormap") as
the related action group is not registered unless the Colormap dockable
is opened. Anyway I mostly needed to get icons and tooltips from these
actions. Let's just copy the strings and be done with it.
This still feel a bit like duplicate code but it's not too bad, so…
I should have cleaned better earlier. :-/
Also add back some horizontal alignment in the colormap entry edit
widgets. This got lost during moving apparently!
This was the original plan since it is closer conceptually to the widget
idea, but I started working on gimp-2-10 where GtkStack was not
available (as it appears in GTK+3).
When loading indexed images, the image type is not obvious at all
(basically only reference is in the title bar). The main issue is that
if you don't realize it when editing, GIMP appear broken when the
expected colors don't appear on canvas.
Commit e48c239459 was a first step by showing various color widgets with
out-of-gamut warnings contextually. This additional commits will also
allows color selection for painting tools (i.e. foreground and
background colors) to be done within the image palette by default. This
way, the fact that this image impose working with limited color palette
is obvious as soon as you try to edit colors.
In GimpTransformGridTool, extend the functionality of the
"Readjust" button, such that if the transformation is already
adjusted to the view (i.e., when the button is clicked the second
time), readjust the transformation to the item bounds (as when
using the tool "normally"), and vice versa. This allows switching
back and forth between "normal" mode, and "adjusted-to-view" mode.
Additionally, disable readjustment when the current transforamtion
is invalid, and show an error when readjustment results in an
invalid transformation.
In gimp_tool_gui_set_response_sensitive(), silently ignore non-
existent response IDs, instead of emitting a CRITICAL, to match the
behavior of GtkDialog and GimpOverlayDialog. This simplifies code
with optional dialog responses.
In the unified-transform, scale, and perspective tools, take the
maximal transform-grid handle size into account when readjusting
the transform, so that the handles themselves are fully within view
under arbitrary rotation, rather than just the corners.
Since the color being currently edited is the selected one, it makes
sense that the fg/bg color should be updated appropriately.
(cherry picked from commit 628960ed22)
In GimpTransformGridTool, reset both transform directions in
response to the "Reset" button, so that the overall transformation
is restored to the identity. Previously, we would only reset the
active transform direction (possibly compensating in the opposite
direction, if both directions are linked). This was intentional,
but it's probably a bit too confusing, especially in conjunction
with the newly added "Readjust" button. Let's just go back to
resetting everything.
Implement GimpTransformGridTool::radjust(), added in the previous
commit, in various transform tools:
The unified-transform, scale, and perspective tools readjust the
transformation such that the grid is centered relative to the view,
and its handles are fully within view under arbitrary rotation.
The rotate tool readjusts the transformation such that the pivot is
centered, and the grid is unrotated, relative to the view.
Add an optional GimpTransformGridTool::radjust() virtual function,
which subclasses can implement to radjust the transformation based
on the current state of the display, such that it's easy to
control. This is especially useful when the image is zoomed-in,
and the transform handles, which are initially across the layer
bounds, are out of view.
When a transform tool implements radjust(), show a "Readjust"
button in the tool GUI. While readjusting the transformation, we
modify the opposite transformation such that the overall transform
remains unchanged, as if both transform-directions were linked, so
that only the transform grid is readjusted.
Do not take "Sample merge" into account when picking colors in a
single-layer image. The reason is to be able to get the index
information on indexed image. This information is lost otherwise when
using the whole image as a pickable.
Of course, other exceptions are possible, when you'd pick exactly a
colormap color, but I don't think it's worth making the code
extra-complicated for these. My previous commit will anyway already
select the right color in the colormap on common cases. Though it will
still fail to select the right index when several indexes store the same
color, on a multi-layer image, if you check "Sample merged" while the
right index was not the first one amongst the duplicates.
The colormap saves colors as unsigned char, which can be very inaccurate
compared to high precision colors. When adding colors from GimpRGB into
the colormap, use the original value to fill the colormap palette
instead of making a round trip conversion from double to uchar, then
back to double.
This also fixes a direct bug I encountered when adding the current
foreground color in the image colormap. Yet the GimpFgBgEditor or the
GimpColorHistory would still show the color out-of-gamut in cases when
the returned RGB after the roundtrip was not close enough to the
original RGB (even despite using an epsilon in GimpPalette code).
Line-art computation can take a long time, and it's therefore
desirable for it to be interruptable. While we do cancel the line-
art async when its result is no longer needed, most parts of the
computation don't respond to the cancelation request, leaving the
async operation running in the background, blocking subsequent
async operations.
Implement cancelation support of line-art computation, by passing
down the async object to the various functions, and periodically
checking for its cancelation at various points. When the async is
canceled, we quickly abort the operation.
Even though cancelation now happens relatively quickly, some parts
of the computation are still uninterruptable and may incur some
latency, so we avoid waiting for the async opration to be aborted
after cancelation, as we did before.
This allows to force a GimpColorArea to display as out-of-gamut color.
Current code was only considering the generic RGB case (outside of [0-1]
range), and in particular not grayscale or indexed images.
Ideally the GimpColorArea widget could be (optionally) made to follow a
context, so that for instance it could update its representation when
the context image changes, or when this image's type changes. Yet since
it is a libgimpwidgets widget, it cannot get such update. Instead I add
a new API function to display the color box with the out-of-gamut
triangle. The decision code for this will have to be done elsewhere.
Use this new API for GimpColorHistory to display non-gray colors in the
history as out-of-gamut on grayscale images, or colors absent of the
palette on indexed images.
... which is equivalent to gimp_parallel_run_async_independent(),
except that it takes an additional "priority" parameter, which
specifies the task's priority, with 0 being the default priority,
and lower values indicating higher priority. Unlike
gimp_parallel_run_async_full(), the priority parameter doesn't
directly control the task's priority in a queue, but rather, we use
it to control the priority of the task's dedicated thread, on
supported platforms (previously, all independent async tasks would
run with low priority.)
Use low priority when loading fonts, which can take a long time, to
keep the existing behavior.
Remove gimp_item_tree_clear(), added in last commit, and move its
code to gimp_item_tree_dispose(). Likewise, in
gimp_image_dispose(), use g_object_run_dispose() on the image item-
trees, instead of gimp_item_tree_clear().
Add gimp_item_tree_clear(), which removes all the items of a
GimpItemTree, and clear the layers/channels/vectors item trees in
gimp_image_dispose(), *before* finalizing the image, so that the
corresponding items' desctructors are called while the image is
still alive. In particular, this allows the destructors to safely
call gimp_item_is_attached(), which happens when the image has a
floating selection, since commit
8d4e5e0ff7.
... in GIMP 2.10.9 from git
In gimppickable-contiguous-region's pixel_difference() function,
which is used, among other things, by the select-by-color and
fuzzy-select tools, when selecting by LCh/HSV hue, treat a pair of
colors as inifinitely far apart if one of them has positive chroma/
saturation, and the other has chroma/saturation that's very close
to 0; conversely, treat a pair of colors as equal if both of them
have chroma/sautation that's close to 0.
As a result, when the seed color is saturated, gray pixels are
never selected, while when the seed color is desaturated, all, and
only, gray pixels are selected.
When clicking on a line art pixel, only this pixel gets colored, which
is fine for actual (original) line art pixels. But on generated ones
(closure pixels, which are internal only), you end up with a single
pixel colored while the whole surrounding area is empty. This feels like
a bug (even though it was not one technically) as you have no way to
guess you are clicking on a closure pixel.
Instead, when this happens, simulate clicks on all neighbour pixels,
hence potentially coloring all surrounding regions, which is most likely
what you wanted.
On various property changes, only recompute the line art when the
property actually changed. Also add a gimp_line_art_bind_gap_length() to
avoid computing twice the line art when changing both type of closure
(splines and segments) together, as is currently the case.
… Bucket Fill tool options.
This will provide feedback when the line art closure is being computed,
which may be useful on big images where it may take some time. Otherwise
painter may be left hanging without knowing what takes time.
Add a "real-time preview" option to the warp tool, which, when
toggled, causes the preview to be rendered synchronously during
motion. This is slower, but gives better feedback.
The "spacing" option of the warp tool used to be handled by the
gegl:warp op, and have little effect. Instead, implement it in the
warp tool directly, having the same effect as the other paint
tools.
Having a properly-working "spacing" option allows us to use EXACT
motion mode without cirppling down performance, which means that
the stroke now follows the pointer exactly, even when processing
takes a while.
Decrease the default "spacing" value to 10.
In the warp tool, use the gegl:map-relative node to calculate the
invalidated drawable area when the displacement field changes, to
account for box filtering.
In gimppaintcore-loops, fix the CanvasBufferIterator algorithm
helper-class so that it may appear more than twice in the
hierarchy, and integrate it into the normal dispatch-dependency
system, instead of having dependent algorithms inherit it directly.
Fix gimp:mask-components to use full-oapcity value for the alpha
component when it's masked-in and there's no "aux" input, so that
the image is rendered with full opacity when the alpha channel's
visiblity is toggled off, as per bug #143315.
In GimpTransformGridTool, fix weak-pointer initialization for the
out-of-bounds selection-boundary canvas item, to avoid a CRITICAL
when initializing the tool in transform-selection mode for a fully
out-of-bounds selection, and a subsequent segfault when re-
initializing the tool.
Add "Constrain handles" and "Around center" options to the
perspective-transform tool's GUI, which are similar to the
corresponding options of the unified-transform tool. Both of these
options can already be controlled using Shift and Ctrl,
respectively, through the transform-grid widget, so we might as
well provide GUI toggles for them.
In GimpToolTransformGrid, use the transformed center-point of the
original polygon as the position of the center-point handle, and as
a snapping point for the pivot when "cornersnap" is TRUE, instead
of using the center-point of the transformed polygon. These two
points are different for non-affine transformations.
Furthermore, when "use-pivot-handle" is FALSE, use the center-
point as the reference point when transforming around the pivot,
instead of the pivot itself, which might not be set. In
particular, this fixes transformation around the pivot in the
perspective tool.
Don't explicitly set the center-point as the pivot in the scale
tool, since this commit makes it unnecessary.
In gimp_pickable_contiguous_region_by_color(), add a small epsilon
to the threshold value, to allow for small errors due to the input
color and pickable pixel-colors being converted to the common
format through different paths.
While we *could* special-case threshold == 0 when the input color
comes from the same pickable, as is the case for the select-by-
color tool, and perform an exact comparison in the original format,
in the more general case the input color can come from an arbitrary
source, such as a plug-in.
In gimp_gegl_create_flatten_node(), explicitly set the output
format of the background gegl:color node according to the composite
space, so that no conversion is required during compositing.
In gimp_image_merge_layers() -- the internal function used by the
various layer-merging/flattenning functions -- process the merged-
layer graph in chunks, using gimp_gegl_apply_operation(), instead
of in one go, using gegl_node_blit_buffer(). Processing in chunks
better utilizes the cache, since it reduces the size of
intermediate buffers, reducing the chances of hitting the swap when
merging large images (see, for example, issue #3012.)
Additionally, this allows us to show progress indication. Have the
relevant gimpimage-merge functions take a GimpProgress, and pass it
down to gimp_image_merge_layers(). Adapt all callers.
Streamline the various action_data_get_foo() functions, by having
each function only match the action data directly against its
specific type(s), and use the other functions to match the action
data against their corresponding types, instead of having each
function directly exhaust all possible matches.
Other than reducing depulication, it fixes certain cases in which
some action_data_get_foo() functions would fail to find a match,
even though one exists, since they failed to exhaust all the
options.
In GimpSmudge, avoid copying the brush's dab to the paint buffer
when using a pixmap brush if the flow parameter is 0 -- it has no
effect in this case.
Reimplement gimp_brush_core_color_area_with_pixmap(), which copies
the brush's dab to the paint buffer when using a pixmap brush, in
terms of gimp-gegl-loops. This simplifies the functions,
parallelizes processing, and transparently handles float brushes.
Replace the "mode" parameter of the function with an "apply_mask"
parameter, which specifies whether to apply the brush's mask to
the dab as part of copying. Avoid applying the mask in
GimpPaintbrush; previously, we would erroneously apply the mask
twice when using the paintbrush tool: once when copying the
dab to the paint buffer, and again when pasting the paint buffer
to the canvas.
We still apply the mask in GimpSmudge, which results in the same
double-application behavior, however, this might be less practical
to fix.
Just the GimpData::save() and ::copy() part that is needed to
duplicate and rename them, the image-to-pipe logic from the export
plug-in remains to be ported.
Replace the use of the deprecated GeglNode::dont-cache property,
and GeglOperationClass::no_cache field, with GeglNode::cache-policy
and GeglOperationClass::cache_policy, respectively.
See commit gegl@7f24430cda0d8c3eff311868823d445edc2a4e12.
The GUI implementation of gimp_wait() relies on the ability to run
plug-ins (namely, the busy-dialog plug-in) without entering the
main loop. This prohibits the said plug-ins from making any PDB
calls, which would result in a deadlock. However, we're currently
calling gimp_gimprc_query() to fetch the prefer-dark-theme option
during gimp_ui_init() (or any time the theme.css file changes).
Instead, communicate this preference through the theme.css file
itself, by writing a /* prefer-dark-theme */ comment to the file
when the option is set. Yes, it's a bit of a hack :P
- don't clear the names of the individual brushes, we need them for
a load -> save roundtrip
- for the same reason, and for convenience, store the parameter string
in the object
- clean up gimp_brush_pipe_finalize()
gimp_widget_flush_expose() has been removed since commit 3089a20167.
I now reimplemented it by simply checking if event sources are waiting
to be processed.
This was heavily needed as the statusbar was not showing any progress
(at least on highly demanding process, such as saving or loading files),
and therefore we were stuck with a seamingly frozen GUI.
Despite the name, this does not apply to a widget in particular anymore,
but to the whole program events.
In gimppaintcore-loops, unsuppress the
COMBINE_PAINT_MASK_TO_CANVAS_BUFFER algorithm (partially
reverts commit b717ead1abd487f663668ac131883dff0ffe4557.)
In gimp_paint_core_paste() it's always used together with
CANVAS_BUFFER_TO_PAINT_BUF_ALPHA, which matches a combined
algorithm, preventing it from being called, however, it can still
be called through gimp_paint_core_replace(), which uses it
together with CANVAS_BUFFER_TO_COMP_MASK, which doesn't have a
combined algorithm. We can, however, filter out
CANVAS_BUFFER_TO_PAINT_BUF_ALPHA whenver
COMBINE_PAINT_MASK_TO_CANVAS_BUFFER is matched (since the combined
algorithm will be matched beforehand when both algorithms are
included).
In gimp_drawable_filter_sync_affect(), don't mask-out the filter's
alpha component when the drawable doesn't have an alpha channel,
since this is no longer necessary -- we now explicitly convert the
output to the drawable format as part of the graph -- and it
prevents the gimp:mask-components node from becoming a NOP.
In gimp_drawable_get_active_mask(), when the drawable doesn't have
an alpha channel, set or clear the mask's alpha bit, according to
the state of the other bits, so that it never gets in the way of a
fully set/clear mask. The value of the alpha bit doesn't matter
when there's no alpha channel, however, having a uniform mask
allows us to skip component masking altogether.
Additionally, provide a default implementation for
GimpDrawable::get_active_mask() which returns a full mask, and
remove the equivalent implementation for GimpChannel.
Remove the mask_components_onto() gimppaintcore-loops function, and
the GimpPaintCore::comp_buffer member. Instead, in
gimp_paint_core_paste() and gimp_paint_core_replace(), use the
MASK_COMPONENTS algorithm, added in the previous commit.
In gimppaintcore-loops, add a new MASK_COMPONENTS algorithm, which
masks the output of compositing into the destination buffer,
according to a component mask. The algorithm uses the same code as
gimp:mask-comopnents, and can be used as part of a
gimp_paint_core_loops_process() pipeline, instead of using a
separate function.
In gimppaintcore-loops, add a CompBuffer algorithm helper-class,
which provides access to the output buffer used for compositing,
to be used by the DO_LAYER_BLEND algorithm instead of the
destination buffer.
CompVuffer itself doesn't provide the storage for the buffer; this
is rather the responsibility of the algorithms that use it. The
TempCompBuffer algorithm helper-class provides temporary storage
for the compositing buffer, and can be used by algorithms that need
a temporary buffer.
In gimppaintcore-loops, use {Mandatory,Supressed}AlgorithmDispatch,
added in the previous commit, to mark certain algorithms as always
occuring, or never occuring, in all hierarchies.
In gimppaintcore-loops, add MandatoryAlgorithmDispatch and
SuppressedAlgorithmDispatch class templates, which implement
dispatch functions suitable for algorithms which are always part of
the hierarchy, or never part of the hierarchy, respectively. Using
one of these classes as the dispatch function for a given algorithm
verifies that the algorithm is/isn't included in the requested-
algorithm set, but doesn't otherwise increase the number of
instanciated hierarchies, since only one of these cases has to be
handled.
In gimppaintcore-loops, remove the individual-algorithm convenience
functions, which are merely wrappers around
gimp_paint_core_loops_process(), and aren't used anywhere anymore.
This allows us to avoid instanciating certain algorithm-hierarchies
which aren't used in practice, as will be done by the following
commits.
Add specialized versions of gimp:mask-components for 8-, 16-, and
32-bpc formats, to improve efficiency, and to preserve the contents
of masked-out components exactly.
Provide public functions for format-selection and processing, which
we'll use in the painting code, instead of reimplementing component
masking.
Mostly I am adding a counter to the insignifiant zone fill, to be double
sure we are not going to fill huge areas (this should not happen already
anyway) and also it is no use to sample the line art buffer in such
case.
In gimp_gegl_apply_cached_operation(), when the source and
destination buffers are the same, avoid duplicating the source
buffer when the applied operation is a point operation, since
applying it in chunks from/to the same buffer is not a problem in
this case.
... which takes a GeglNode, and determines if the associated
operation is a point operation.
Use in GimpFilterTool, instead of performing the same check
manually.
When the result of compositing has an alpha value of 0, the
corresponding color value is not mathematically defined.
Currently, all out layer modes opt to preserve the destination's
color value in this case. However, REPLACE mode is different
enough to warrant a different behavior:
Unlike the other layer modes, when the compositing opacity
approaches 0 or 1, the output color value approaches the
destination or source color values, respectively, regardless of the
output alpha value. When the opacity doesn't approach 0 or 1, the
output color value generally doesn't approach a limit as the output
alpha value approaches 0, however, when both the destination and
source alpha values are equal, the output color value is always a
simple linear interpolation between the destination and source
color values, according to the opacity. In other words, this means
that it's reasonable to simply use the above linear interpolation
for the output color value, whenever the output alpha value is 0.
Since filters are commonly combined with the input using REPALCE
mode with full opacity, this has the effect that filters may now
modify the color values of fully-transparent pixels. This is
generally desirable, IMO, especially for point filters. Indeed,
painting with REPLACE mode (i.e., with tools that use
gimp_paint_core_replace()) behaved excatly as described above, and
had this property, before we switched gimp_paint_core_replace() to
use the common compositing code; this created a discrepancy between
painting and applying filters, which is now gone.
A side effect of this change is that we can now turn gimp:replace
into a NOP when the opacity is 100% and there's no mask, which
avoids the compositing step when applying filters. We could
previously only apply this optimization to PASS_THROUGH mode, which
is a subclass of REPLACE mode.
Note that the discussion above concerns the UNION composite mode,
which is the only mode we currently use REPLACE in. We modify the
rest of the composite modes to match the new behavior:
CLIP_TO_BACKDROP always preserves the color values of the
destionation, CLIP_TO_LAYER always preserves the color values of
the source, and INTERSECTION always produces fully-zeroed pixels.
Remove gimp_gegl_replace(), which is not used anywhere since the
last commit. It's redundant with the rest of our compositing code,
in particular, gimp:replace and gimp:mask-components.
Remove gimp_drawable_replace_buffer(), which is no longer used
anywhere since commits ddb69b77a7 and
3451ffb62c. This eliminates
redundancy, since all compositing is now done through the layer-
mode code.
Furthermore, gimp_drawable_replace_buffer() used the drawable's
active-component array, whose layout depends on the image mode, as
an argument to gimp_gegl_replace(), which always expects an RGBA
component array, resulting in broken component masking in non-RGB
images.
The algorithm to compute a zone area by following its border only works
well for fully closed zones. It may return negative values otherwise.
Let's just assume the created zone is big in this case (which may or may
not be the case, but this is the safe case as it does not prevent
closure creation).
`icon_space_width` is set when GtkWidget::style-updated signal is
emitted. In some cases, it was possible that gimp_statusbar_set_text()
is run before this happens, in which case, a division by zero would
crash the software.
I encountered this case in some rare occasions when duplicating an image
with ctrl-d, then as Ctrl was hold, the message "Click in any image to
pick the foreground color" was pushed on the brand new statusbar as it
is created (hence a race condition occurs with the signal handler and
this message). This was therefore not reproducible every time, but easy
enough to reproduce with multiple tests.
I realized that the same issue as for indexed images could also apply to
grayscale. If your fg/bg colors are not gray, it should not be expected
for them to be paintable. So let's give the out-of-gamut hint.
If a color is not within the indexed image's palette, we can consider it
to be out-of-gamut too (the gamut of such image being its palette).
This is a first step towards fixing #2938. Basically currently opening
indexed images is not made obvious and you can end up thinking GIMP is
broken as when you try to paint with a given FG or BG color, you may get
a completely different color on the canvas. And it is not obvious to
realize why. Now at least, the FG/BG color will tell you when the color
you are trying to paint with is not within the accepted palette.
Current implementation was always making 2 rows. Yet sometimes the dock
can be made wide enough to accomodate all the color icons on 1 row,
hence saving vertical space, as well as not leaving this ugly space on
the side of the widget.
Now the widget will be able to compute an optimal size. Therefore if
there is enough space to align all color icons on a single row, this
will be organized this way. Otherwise, the widget will request more
vertical space to use 2 rows, hence allow a narrower dock as well.
Basically this will adapt to how you organize your docks, depending on
whatever screen size, pixel density, etc.
This widget was not working well without setting a size request. In
particular, in the color editor, if we were not packing with expand and
fill to TRUE, then it ended up as an invisible widget (too small to be
seen). On the other hand, with expand/fill, it ended up taking as much
place as possible, and in particular expanding horizontally in an ugly
way.
First of all, I make it a width-for-height widget, so that it keeps by
default a pretty ratio (the width must be a bit bigger than the height).
Then I give it a default size request because this widget makes no sense
if it is too small (it packs quite a lot of elements with fg and bg
colors, swap and default colors icons).
Finally I can remove the expand/fill request in the GimpColorEditor. And
now the fg/bg widget appears with a nice ratio.
Note: this is GTK+3 only commit as the size request mode didn't exist
back in GTK+2.
There was no use to pack them on 2 lines. It was just making the FG/BG
widget overly huge, and the color picker button and hex entry very long.
This was just ugly. Also this dock is tall enough, so packing things
horizontally when it makes sense is better.
but only the actual saving code, not the export magic and dialog.
Add new internal procedure file-pat-save-internal which is not
registered as a file procedure and always works non-interactively on
the passed arguments and only saves the passed drawable. Use the new
internal procedure from the file-pat-save code and remove all file
writing code from the plug-in.
This way all pattern file writing code duplication is killed, while
the whole export mechanism is completely unchanged.
In the applicator path of gimp_paint_core_replace(), actually use
the paint-core's applicator, instead of using
gimp_drawable_replace_buffer(). This improves speed, consolidates
code, and fixes some cases in which the latter is broken.
Furthermore, when using CONSTANT paint application-mode, use the
paint-core's undo_buffer as the compositing source, rather than the
drawable buffer, which is the correct behavior.
In the applicator path of gimp_paint_core_paste(), use the paint
mask directly when combining it to the canvas buffer, rather than
using a copy of it, since it's not being modified.
Fix some comments.
Implement the no-applicator path of gimp_paint_core_replace() in
terms of gimp_paint_core_loops_process(), using the algorithms
added in the previous commit, instead of using
gimp_drawable_replace_buffer(). This improves speed, consolidates
code, and fixes some cases in which the latter is broken.
Furthermore, when using CONSTANT paint application-mode, use the
paint-core's undo_buffer as the compositing source, rather than the
drawable buffer, which is the correct behavior.
In gimppaintcore-loops, add CANVAS_BUFFER_TO_COMP_MASK and
PAINT_MASK_TO_COMP_MASK paint algorithms, which copy the canvas
buffer and the paint mask, respectively, to the compositing mask.
When there is an image mask buffer, the algorithms additionally
combine the copied mask with the mask buffer. When possible, the
algorithms use the canvas buffer/paint mask data directly as the
compositing mask data, instead of copying.
These algorithms are necessary in order to implement
gimp_paint_core_replace() in terms of
gimp_paint_core_loops_process(), which is done by the next commit.
In gimppaintcore-loops, in the DO_LAYER_BLEND paint algorithm, and
in the CanvasBufferIterator algorithm helper-class, initialize the
iterator *before* initializing the base class, to make sure that
dest_buffer, or canvas_buffer, respectively and in that order, is
the primary buffer of the iterator. In particular, this avoids the
mask buffer being the primary buffer. This is desirable, since
most of the buffers we iterate over are tile-aligned to the dest/
canvas buffers.
In gimppaintcore-loops, add a MaskBufferIterator algorithm helper-
class, which provides read-only iterator access to the mask buffer,
if one is used.
Use the new class in DoLayerBlend, instead of manually managing the
mask-buffer iterator.
In gimppaintcore-loops, add a CompMask algorithm helper-class,
which provides access to the mask buffer used for compositing, to
be used by the DO_LAYER_BLEND algorithm instead of the image mask
buffer.
CompMask itself doesn't provide the storage for the mask; this is
rather the responsibility of the algorithms that use it. The
TempCompMask algorithm helper-class provides temporary storage for
the compositing mask, and can be used by algorithms that need a
temporary mask.
In gimppaintcore-loops, add finalize() and finalize_step()
algorithm functions, which get called at the end of processing the
entire area, and at the end of processing each chunk, respectively.
Algorithms can use these functions to clean up allocated resources.
In the various gimppaintcore-loops algorithms, assign each
algorithm's iterator indices dynamically, rather than statically,
so that algorithms have more freedom in the way they initialize the
iterator.
Add GimpData::save() implementation to GimpPattern, and change some
glue code to make patterns editable.
Also implement GimpData::duplicate() in GimpPatternClipboard, which
makes it possible to simply copy an area and duplicate the clipboard
pattern to create a new persistent pattern.
Add some comments and string docs as it is not that obvious to
understand the whole logics, invert the return value (returning TRUE
when the closure line is accepted, instead of the opposite) and rename
it to more appropriate gimp_line_art_allow_closure().
We currently have brush and pattern I/O code in both the core and
plug-ins. This commit starts removing plug-in code in favor of having
one copy of the code in the core, much like XCF loading and saving is
implemented.
Add app/file-data/ module with file procedure registering code, for
now just with an implementation of file-gbr-load.
Remove the file-gbr-load code from the file-gbr plug-in.
The initialization is sometimes done when switching tools (for instance
when selecting the active tool by command), not always on button press.
So the error output behavior was inconsistent, and worse, the tool was
sometimes not forbidden to run when it should have been.
Just run all the checks (layer groups, locks and whatnot) on button
press.
Again some missing context when requesting the strings (while they were
declared with context in static NC_()).
Also some mixup with some zoom actions strings declared with different
context in the same GimpEnumActionEntry.
The line art imaginary segments/splines are not added when they create
too small zones, unless when these are just too small ("unsignificant").
Why the original algorithm keeps such micro-zones is because there may
be such zones created when several splines or segments are leaving from
a same key point (and we don't necessarily won't to forbid this). Also
we had cases when using very spiky brushes (for the line art) would
create many zones, and such micro-zones would appear just too often
(whereas with very smooth lines, they are much rarer, if not totally
absent most of the time).
Also it is to be noted that the original paper would call these
"unsignificant" indeed, but these are definitely significant for the
artists. Therefore having to "fix" the filling afterwards (with a brush
for instance) kind of defeat the whole purpose of this tool.
I already had code which would special-case (fill) 1-pixel zones in the
end, but bigger micro zones could appear (up to 4 pixels in the current
code, but this could change). Also I don't want to use the "Remove
Holes" (gimp:flood) operation as I want to make sure I remove only
micro-holes created by the line art closure code (not micro-holes from
original line arts in particular).
This code takes care of this issue by filling the micro-holes with
imaginary line art pixels, which may later be potentially bucket filled
when water-filling the line art.
Plug-ins are not prepared to handle high-precision brushes/
patterns, even when they're otherwise aware of high-precision
drawables, so make sure to always use compat formats when
communicating brush/pattern data to plug-ins.
Allowing plug-ins to handle high-precision brush/pattern data would
require some additional API.
Avoid unnecessary calls to gimp_temp_buf_data_clear() in various
places, where either the entire buffer is being written to, or most
of it is, only requiring clearing the edges.
Promote the precision of generated brushes to 32-bit float, and
modify brush preview generation, and gimpbrushcore-loops, to handle
float brushes. This avoids posterization in large brushes.
Note that non-generated brushes are still uint8.
In GimpTempBuf, add gimp_temp_buf_lock() and gimp_temp_buf_unlock()
functions, which lock/unlock the buffer for data access. Unlike
gimp_temp_buf_get_data(), which returns a direct pointer to the
buffer's data, the new functions take a format parameter and may
return a temporary buffer, allowing the buffer to be accessed using
an arbitrary format.
Older --enable-binreloc configure option had basically the same purpose
as the newer --enable-relocatable-bundle, though the old binreloc was
only used for gimpenv.c code.
As a consequence, commit 10ce702188 was still not working fine since
gimp_installation_directory_file() also need binreloc enabled (to be
actually relocatable).
Let's get rid of this whole mess, by implying we want binreloc code to
be used when --enable-relocatable-bundle is ON. We don't need the
m4macros anymore, since AM_BINRELOC was basically just checking that
`/proc/self/maps` was present. But anyway being present at compile time
does not mean it will be at runtime (nor the opposite). So this test is
not that useful. The binreloc code will anyway fallback gracefully to
the non-binreloc code (i.e. trying to use build-time install paths) if
the procfs is lacking at runtime.
Add a GimpTransformGridTool::matrix_to_info() virtual function,
which should extract the tool-specific transformation parameters
given a transformation matrix, and the old parameter set (which is
needed in some tools, to derive the parameters that aren't encoded
in the matrix, such as the pivot point). The transformation matrix
can be any combination of matrices calculated by the tool, and
their inverses. Subclasses should only implement this function if
every such matrix can be mapped back to transformation parameters.
This is currently the case for all the transform-grid tools, except
for the shear tool (since it only supports shearing along one of
the horizontal or the vertical directions, however, the combined
matrix may require shearing in both directions).
When a transform-grid tool implements this function, show a chain-
button between the two transform-direction radio-buttons in the
tool options. When the chain-button is linked, whenever the
transform corresponding to the active direction is modified, adjust
the transform corresponding to the non-active direction such that
the overall transform remains the same.
One notable workflow that this enables is transforming a layer
while adjusting a different area than its boundary, by first
defining the area while the transform-directions are linked, and
then transforming the area while the transform-directions are
unlinked.
In GimpTransformGridTool, allow performing simultaneous forward
(normal) and backward (corrective) transforms, by having each
transform direction operate on an independent set of parameters.
In other words, whereas the transform-grid tools previously had a
single transform, which could be applied either normally or
correctively using the "direction" tool-option, they now have two
independent transforms, one applied normally and the other
applied correctively, which are toggled using the "direction"
option. The overall transform is the combination of the backward
transform, followed by the forward transform.
Another way to think about it, is that the tool transforms a source
shape into a destination shape. The source shape is defined by the
backward transform, and the destination shape is defined by the
forward transform. Wherewas previously only one of these shapes
could be controlled (the other shape always being the item bounds),
it's now possible to control both shapes in a single transform.
The next commit will allow modifying both shapes simultaneously,
making this even more useful.
Note that since both transforms start off as the identity, using
only one of the transform directions has the same behavior as
before.
Add an undo_desc field to GimpTransformToolClass, which subclasses
should set to the tool's default undo description. Provide a
default implementation for the get_undo_desc() vfunc, which returns
(a copy of) undo_desc. This simplifies transform tools that have a
static undo descrption, as well as provides a fallback when a
detailed undo description can't be generated (not currently
relevant, but will be used in the next commit).
When rotating an item around its center using the rotate tool,
i.e., if the pivot point hasn't been moved, don't include the pivot
coordinates in the undo description.
In GimpToolCompass, add a read-only "effective-orientation"
property, which returns the actual orientation of the compass; in
particular, if the "orientation" property is set to AUTO,
"effective-orientation" returns HORIZONTAL or VERTICAL, depending
on the current compass direction. In 3-point mode, the property
always returns AUTO.
In GimpMeasureTool, don't try to access the "straighten" button
when halting the tool if it's NULL, which can happen when the
measure tool is selected upon startup, but is changed before its
tool-options GUI is constructed.
In gimp_transform_matrix_generic(), apply the resulting matrix even
if the transformation is invalid, since GimpGenericTransformTool
relies on the matrix to properly update the transform-grid widget.
In GimpTransformGridTool, when the "show-preview" tool-option
changes, don't take the transform validity into account when
deciding whether to hide the current layer -- it should only affect
the visibility of the preview, not the layer.
...1920*1980 resolution
In GimpOperationTool, make the generated GUI scrollable if it is
higher than half the monitor's workarea. This is meant as a last
resort for generated GUIs that do not have a custom constructor that
makes them usable using better layouts.
gimplanguagestore-parser.c (parse_iso_codes): instead of
special-casing Windows and OS X, use ENABLE_RELOCATABLE_RESOURCES and
find the package relative to ${gimp_installation_directory}, so
relocating it works on all platforms (also flatpack, snap, whatever),
given the --enable-relocatable-bundle configure switch is used.