In GimpAction, instead of connecting the action-history log
function to the action's "activate" signal as a user-provided
handler, call it directly from the default handler.
In subclasses of GimpAction, chain to the parent's activate()
function before emitting the "selected" signal, so that we always
log the action in the history before responding to it.
This allows us to avoid the hack in commit
6544ce4301.
In xcf_write_int8(), avoid calling g_output_stream_write_all() with
data == NULL and count == 0, in which case it raises a CRITICAL and
doesn't set bytes_written, which we proceed to use uninitialized.
This can happen, e.g., when writing an empty parasite.
In gimp_color_profile_new_from_icc_profile() and
gimp_image_validate_icc_profile(), don't raise a critical when
encountering an empty profile, but rather reject it gracefully with
an error.
... (Invalid read reported by valgrind)
In gimp_image_parasite_validate(), don't segfault when validating
a "gimp-comment" parasite of size 0 (i.e., whose data is a 0-byte
array, not an empty string), and just consider it invalid.
... (Invalid read reported by valgrind)
In xcf_read_int8(), avoid calling g_input_stream_read_all() with
data == NULL and count == 0, in which case it raises a CRITICAL and
doesn't set bytes_read, which we proceed to use uninitialized.
This can happen, e.g., when reading an empty parasite.
In gimp_action_history_activate_callback(), bail if history.gimp is
NULL, instead of dereferencing it. This can happen if GIMP is shut
down during the execution of a temporary procedure, such as a
script-fu script. See the code comment for details.
In GimpMeter, use cairo_path_extents() for getting the history
graph's extents, rather than cairo_clip_extents(), since the latter
may also include clipping applied by GTK.
I realize that g_file_has_parent() is only looking for the direct
parent. I want to look for any ancester, so I loop through parents (with
a limit to avoid infinite looping with symlinks and such).
This is quite important since it will allow extensions to support only
some versions of GIMP (not trying to run a plug-in made for an
older/newer libgimp API for instance, or using new features, etc.).
It will also allow extensions to add dependencies to other extensions.
Extensions work for brushes, dynamics, MyPaint brushes, patterns,
gradients, palettes and tool presets.
More to come, but this is a first and working proof-of-concept.
Right now it only loads AppStream data, which is completely useless, yet
is a base of a managed extension system. Having proper metadata is what
will allow to actually know what is installed.
This is only the first draft.
Note that I am not adding the extension path into GimpCoreConfig on
purpose, since the point is not to have people manage their extension
directories manually anymore.
The extensions will be loaded from the build-time system path or the
config directory, and that's all.
What will probably be stored in the config though will be the remote
repositories URLs (allowing third-party extension repositories).
According to some bug reports, it seems that under some (unknown)
conditions we might save an empty custom gradient file on exit (for
equally unknown reasons). The only difference in the way we save
internal data files, such as the custom gradient, compared to
gimp_data_save(), is the fact that we currently don't explicitly
close the output stream, but rather only unref it.
The output stream should be implicitly closed (and hence flushed)
upon destruction, but maybe the unreffing is not enough to
guarantee that it's actually destroyed (maybe it spawns an extra
reference for some reason, who knows.) Anyway, let's just
explicitly close it, which also gives us a chance to catch and
report any errors occursing during flushing/closing (which,
altenatively, might be the culprit).
Additionally, a few more error-reporting improvements, to match
gimp_data_save().
Add an "async" field to the dashboard's "misc" group, showing the
number of async operations currently in the "running" state (i.e.,
all those GimpAsync objects for which gimp_async_finish[_full]() or
gimp_async_abort() haven't been called yet).
Preview generation for layer groups is more expensive than for
other types of drawables, mostly since we can't currently generate
layer-group previews asynchronously. Add a preferences option for
enabling layer-group previews separately from the rest of the
layer/channel previews; both of these options are enabled by
default. This can be desirable regardless of performance
considerations, since it makes layer groups easily distinguishable
from ordinary layers.
In the preferences dialog, make the "dither images when promoting
to floating point" option insensitive when the "promote impoprted
images to floating point precision" option is unchecked.
In GimpViewRendererDrawable, use
gimp_drawable_get_sub_preview_async(), added in the previous
commit, to render drawable previews asynchronously. While the
preview is being rendered, either keep showing the previous
preview, or render a placeholder icon.
This commit also fixes an issue where, under certain conditions, a
drawable preview would be rendered, even when layer/channel
previews are disabled in the preferences.
... which is an asynchronous version of
gimp_drawable_get_sub_preview().
We currently support async preview generation for drawables whose
buffer isn't backed by a GimpTileHandlerValidate tile handler
(i.e., anything other than group layers), since preview generation
fir such drawables may involve processing the corresponding graph,
which isn't thread-safe.
When the GIMP_NO_ASYNC_DRAWABLE_PREVIEWS environment variable is
defined, all drawable previews are synchronously generated.
Remove the "independent" parameter of gimp_parallel_run_async(),
and have the function always execute the passed callback in the
shared async thread-pool.
Add a new gimp_parallel_run_async_full() function, taking, in
addition to a callback and a data pointer:
- A priority value, controlling the priority of the callback in
the async thread-pool queue. 0 is the default priority (used
by gimp_parallel_run_async()), negative values have higher
priority, and positive values have lower priority.
- A destructor function for the data pointer. This function is
called to free the user data in case the async operation is
canceled before execution of the callback function begins, and
the operation is dropped from the queue and aborted without
executing the callback. Note that if the callback *is*
executed, the destructor is *not* used -- it's the callback's
responsibility to free/recycle the user data.
Add a separate gimp_parallel_run_async_independent() function,
taking the same parameters, and executing the passed callback in
an independent thread, rather than the thread pool. This function
doesn't take a priority value or a destructor (and there's no
corresponding "_full()" variant that does), since they're pointless
for independent threads.
Adapt the rest of the code to the changes.
- remove gimp_widget_flush_expose()
- remove the "now" argument to gimp_display_shell_flush() and make it
only update widget states
- rename gimp_display_flush_whenever() to gimp_display_flush_update_region()
and call gimp_display_shell_flush() separately in the only case we
passed FALSE to flush_whenever()
- remove th flush_now interval logic from GimpDisplay, as soon as we
have exposed the canvas, we are in the loop for the next frame clock
tick anyway, so delaying a useless and removed process_updates serves
no purpose
- in gimptool-progress.c, create the invisible grab widget also for
non-cencelable cases, so we can always safely run the main loop
manually to make the progress updates visible
- in gimp-gegl-apply-operation.c, always run the main loop manually
to make the progress updates visible
- in gimpstatusbar.c, leave some FIXME comments as reminder that
we might need the same logic as in gimptool-progress.c
and remove all other tool options parent setting/unsetting and
property copying code. Also select a tool at the end of
tool_manager_init() so it is in sync with what the tool options
manager does.
tool_options_manager_tool_changed(): also copy the non-global paint
options of the new tool to the global paint options, so they get used
when "global_foo" is enabled.
tool_options_manager_paint_options_notify(): sync properties between
tool paint options and global paint options if the property is global
*or* the active tool is involved.
tool_options_manager_global_notify(): don't mess with the active
tool's connection to the user context or its properties at all, it is
always fully connected to the user context anyway.
The way we currently manage tool options, and particularly copy things
around for "global_brush", "global_pattern" etc. sucks, is spread
across files, happens only on tool change, is thus buggy and leads to
all sorts of small inconsistencies.
This new manager will replace all of that stuff, and it does it in one
place, and will keep the user context, the global paint options, and
all tool options connected permanently, and only connect/disconnect
things when settings change, so everything is always in a consistent
state.
Pushed for review only, nothing is used yet.
In gimp_data_factory_finalize(), wait on the factory's async set
after canceling it, and before continuing destruction. It's not
generally safe to just abandon an async op without waiting on it
-- this is a font-specific hack, due to the fact we can't actually
cancel font loading, and GimpFontFactory is prepared to handle
this.
Instead, in gimp_font_factory_finalize(), cancel and clear the
async set, so that GimpDataFactory doesn't actually wait for
loading to finish.
In gimp_font_factory_load_async_callback(), don't try to acess the
factory when the operation is canceled, since cancelation means the
factory is already dead. On the other hand, when the opeation
isn't canceled, make sure to thaw the container even when font
loading failed, so that we always match the freeze at the begining
of the operation.
The "move-current" flag does not apply to selection mode and our current
code was simply setting the radio buttons insensitive while showing the
same labels on both buttons. This was not wrong per-se, yet very
confusing.
Instead let's just hide the radio buttons in selection mode, and update
the option title to "Move selection" (old label of both buttons) to keep
this mode as understandable as possible.
When defining a dashboard group, allow specifying multiple
variables as input to the group meter's LED. The LED is active
when any of the specified variables evaluates to TRUE, and its
color is the combination of the active variable colors.
Remove the swap-busy variable, and use swap-reading and
swap-writing as inputs to the swap group's LED instead, so that the
LED's color indicates whether the swap is currently being read-
from, written-to, or both.
GIMP_BRUSH_MAX_SIZE was already defined (as 10.000 pixels per dimension,
which is big for a brush) in gimpbrush.h. Let's just use this to
validate the size returned by the header.
which means we can't setup scrollbars there. Move the code to a
GtkTickCallback which runs before the next frame after the
size-allocate.
Also put the center_image_on_size_allocate() code there because it has
to run after the canvas' tick callback, and the order of tick
callbacks can't be controlled.
As a side effect we now have a flag in GimpDisplayShell which
indicates that there will be a size allocate before the next frame, so
simply skip drawing the canvas completely. This fixes new images
jumping around when they are first shown.
The flag `free_selection_string` is used to track an array of strings
with some of them being static and others allocated. This should have
been an array of boolean but we can't change it because it is public API
(though it should really not have been!).
So let's just allocate every string of the `selection` array instead,
which makes the boolean flag useless now.
(cherry picked from commit b585201e5e)
... --enable-relocatable-bundle option.
This will allow to use this option for more than MyPaint brushes. For
macOS and Windows, we default to "yes" and "no" for other OS, though it
is always possible to set an explicit value.
We should not have essential signal connections (such as setting tool
options from brush properties) implemented in the tool options GUI
files, because they are not active until the options GUI is created.
Also, that magic is simply too hidden in the options GUI files.
Move the signal connections and the brush property copying code to
gimppaintoptions.c where is can also be done cleaner.
However, this must only be done for the main tool options instance
that is used for the GUI. Therefore, add a "gui_mode" boolean to
GimpToolOptions and set it to TRUE for all main tool options.
(this is ugly, but much less ugly and much less hidden than all the
places where code lives (like tool_manager.c) that can now be moved
into GimpToolOptions and its subclasses, and implemented cleanly
there).
Actually since we make this function so that it should never return
NULL, we may as well return a test at the end. If pixbuf is NULL, then
it is an implementation bug somewhere and we should fix it.
Massimo spotted some warning with clang in #1608 about pixbuf being used
initialized. Rather than just initializing it, I am actually
reorganizing a bit more the function because there was a bit of a logics
bug. In some weird case, it would have still been possible for this
function to return NULL instead of a magenta square (the case was: the
icon was not present in the icon theme; then wilber-eek was either not
present or failed to load).
This new code organization is more clearer, as a step by step, should
better identify the various failure cases and always return an allocated
GdkPixbuf.
This is the only place where such a check occured. All other calls
seemed to expect the return value to be non-NULL already.
(cherry picked from commit a9d851070a)
When the "gimp-wilber-eek" fallback will fail to load, we just create an
ugly magenta square instead.
See Mitch's review at #1608.
Master adaptation for commit 32931c4606.
We were already doing so when an icon was simply absent from the icon
theme. But we may still end up in cases where the icon is seemingly
present, yet it fails to load (for instance the image file is
corrupted). When this happens, let's also try to load the wilber-eek
fallback.
Note that it doesn't completely stops gimp_widget_load_icon() from
possibly returning NULL (in the case where "gimp-wilber-eek" is also
missing/corrupted for instance), so calling code must still account for
possible NULL return value.
Basically the same as commit 9c6237b182
except that I just redid it instead of cherry-picking because the code
was a bit too different.
If "gimp-swap-colors" or "gimp-default-colors" are present in the theme,
yet broken somehow, GIMP would crash because it was not checking if the
icons had been successfully loaded.
Just make the relevant checks and output on standard error that the swap
and/or default color areas are invisible.
(cherry picked from commit d997b2b897 with
conflicts resolved)
Let the split and replicate segments dialogs keep their own data and
don't use GimpGradientEditor struct members. Remove redundant members
and indent the struct.
- remove redundant frames, 3d-frames are gone anyway, so no need to
keep double out/in frames around
- give all color selector classes CSS names
- add/fix some theme CSS styles
Set the tooltip in GimpColorHexEntry itself and remove all other
tooltip setting. This just moves the translatable string sround in
libgimpwidgets/, and even removes it from app/.
Not sure this is really solving the issue reported, which is that
`g_get_tmp_dir()` uses environment variables (yet as g_file_open_tmp()
uses g_get_tmp_dir()…). But at least g_file_open_tmp() should create
unique temporary files, which prevents overriding existing files (which
is most likely the only real attack possible here, or at least the only
one I can think of unless some weird vulnerabilities exist in glib).
Return an optional tooltip from gimp_procedure_get_sensitive(), in
GimpPlugInProcedure, build that tooltip from the image types the
procedure works on.
... to satisfy the inter-variable dependencies, so that the READING/
WRITING variables are based on the READ/WRITTEN values of the
current sample, and not the previous one.
... and gimppaintcore-loops
When a rectangle argument is NULL, use the extents of the
corresponding buffer, instead of raising a CRITICAL, to match the
old behavior.
Add "read" and "written" fields to the dashboard swap group, which
report the total amount of data read-from/written-to the tile swap,
respetively. These fields are non-active by default. When these
fields are active, show a color underlay in the swap group's meter,
indicating when data was beging read-from/written-to the swap.
Improve the swap busy indicator (used as the meter's LED), so that
it's active whenever data has been read-from/written-to the swap
during the last sampling interval, rather than at the point of
sampling.
Use gimp_input_data_stream_read_line_always(), instead of
g_input_data_stream_read_line(), in a bunch of places that don't
expect EOF. If we don't do that, the code assumes the GError
parameter is set by the function and returns an error indication,
causing the caller to segfault when it tries to access
error->message. Instead, we now process an empty line when EOF is
reached, which is caught by the normal parsing logic.
Additionally:
- Use gimp_ascii_strto[id]() when loading gradients, generated
brushes, and palettes, to improve error checking for invalid
numeric input.
- Improve gradient-segment endpoint consistency check.
- Allow loading palette files with 0 colors. They can be created
during the session, so we might as well successfully load them.
... which are similar to g_ascii_strtoll() (except that
gimp_ascii_strtoi returns a gint, and not a gint64), and
g_ascii_strtod(), however, they make error checking simpler, by
returning a boolean value, indicating whether the conversion was
successful (taking both conversion and range errors into account),
and return the actual value through a pointer.
... which is a drop-in replacement for
g_data_input_stream_read_line(), however, it always returns a non-
NULL value when there's no error. If the end-of-file is reached,
an empty string is returned.
...while other windows are on the second monitor if window positions
are saved at exit
Add some lines of code to color_area_color_clicked() which position
the already existing color dialog exactly like a newly created dialog
would be positioned by gimp_dialog_factory_add_dialog().
This should be part of GimpDialogFactory but let's wait for another
case before we generalize it.
When using the color-picker tool's info window, set the tool's
display when a color is picked, and the gui is updated, so that we
properly halt the tool when the display is closed. Otherwise, we
may segfault.
The memory group shows memory-usage information: the currently used
memory size, the available physical memory size, and the total
physical memory size. It can also show the tile-cache size, for
comparison against the other memory stats. The memory group is
active but contracted by default.
Note that the upper-bound of the meter is the physical memory size,
so the memory usage may be > 100% when GIMP uses the swap.
This is currently implemented for *nix systems with Linux-like
procfs, and Windows.
gimp_layer_create_mask(): make sure we don't do a gamma conversion
when initializing the mask from a channel. This was probably not the
last place to need this fix.
Also get rid of a second switch(add_mask_type), must be some leftover
from long gone logic.
...upon exporting an image
Step 1: make it configurable just like "Export EXIF" etc.
app, libgimp: add "export-color-profile" config option
Add it to the preferences dialog, and pass it on to plug-ins in the
GPConfig message. Add gimp_export_color_profile() to libgimp.
Nothing uses this yet.
... with a color profile other than the gimp built-in.
When initializing the smude tool's accum buffer, use
gimp_pickable_get_pixel_at(), instead of
gimp_pickable_get_color_at(), for picking the initial color to fill
the buffer with, so that we don't erroneously apply the image's
color tranform to it when the image has a profile. Previously,
this would result in wrong colors when painting from the drawable
edges inward, with flow < 100%.
gimp_eek(): simply increase gimp->busy so XCF saving calling
gimp_set_busy() and gimp_unset_busy() won't call the GUI layer and do
whatever windowing system calls to set busy cursors.
... with a color profile other than the gimp built-in.
Remove the separate alpha-channel copy in
gimp_image_color_profile_srgb_to_pixel(). We no longer need it,
since GimpColorTransform already takes care of that itself.
We used babl_process() to copy the alpha, which would also
transform the input color from R'G'B' to the output color space.
When the same buffer was used for both input and output, this call
would overwrite the input to the subsequent
gimp_color_transform_process_pixels() call; when the output color
space was different than R'G'B', this meant we'd pass the input to
gimp_color_transform_process_pixels() in the wrong color space,
producing wrong results. This was the case when converting the
foreground color for use with the smudge tool.
resize_dialog_new(): create the preview with "popup = TRUE", so we
really get a preview of layer size and not of the layer within the
image context like used for the layers dialog.
and remove all other calls to gtk_widget_show(child). We used to do
this in gimp_dockbook_add() before (which is gone), and missed to add
a few show(). Since we have no invisible pages, simply show them
gemerically.
when the GimpDeviceInfo already has a device. This is not a programming
error that should trigger a bug report popup, it's something else about
non-uniqueness of device names, or whatever. Simply g_printerr() a more
useful message that can help to debug this and bail out.
...after program restart
GimpContext was always supposed to keep the names of objects (brush,
pattern, font etc.) around even if these objects don't exist, for
cases like refreshing the data in a GimpDataFactory (which worked
fine), but also for deserializing the names of objects which don't
exist *yet* (delayed loading, no-data or whatever).
This commit fixes the delayed loading case (particularly affects fonts):
gimp_context_deserialize_property(): always keep the name of the
object around when it is not found, not only in the no-data case.
gimp_context_copy_property(): always copy the object *and* its name to
the dest context.
Add GimpConfig::duplicate() and ::copy() implementations which chain
up for duplicating/copying all properties and additionally copy all
object names to the new/dest context.
It seems it was simply forgotten. PROP_MASK_ALL is used at some very
central places, so this commit might fix a few subtle bugs, or
introduce new ones, everybody look for strange tool preset behavior
please :)
Same as we did for binfmt-style lines, if the executable part (for
instance in `pygimp.interp`) is not an absolute path, let's allow
ourselves to find it in the environment $PATH.
(cherry picked from commit f3c5ed55c8)
So basically our binfmt set for Python simply never worked since we just
set 'python' and not a full path, but current code was not looking in
the $PATH environment. This was dead code. Now it's fixed.
(cherry picked from commit 6080178a39)
Current code of gimp_interpreter_db_resolve() was only resolving the
interpreter by the file extension when the file could not be opened for
reading or if it was empty/coult not be read. This basically made this
test completely useless.
Let's fix this. Now it will be run all the time, but simply at the end,
if shebang and magic failed.
(cherry picked from commit 8509117fe6)
In GimpScaleTool, fix scaling around the center-point, and make
sure the width and height are always >= 1 when updating the
transformation in response to a widget change.
Get rid of GimpTransformGridTool::recalc_matrix() and
gimp_transform_grid_tool_recalc_matrix(), and have
GimpTransformGridTool and its subclasses use
GimpTransformTool::recalc_matrix() and
gimp_transform_tool_recalc_matrix() directly instead.
In order to break the GimpToolWidget::changed/
GimpTransformTool::recalc_matrix() loop, add a
GimpTransformGridTool::update_widget() vfunc, which subclasses
should override to update their tool-widget (instead of doing this
in ::recalc_matrix()), and ::widget_changed(), which is called when
the tool-widget changes (and which subclasses should override
instead of connecting to the tool-widget's "changed" signal
directly.) GimpTransformGridTool calls these functions as
necessary, instead of relying on extra parameters passed to
recalc_matrix().
Adapt all the direct and indirect subclasses of
GimpTransformGridTool to the change.
In GimpTool, always clear tool->drawable upon halting, even for
tools that don't use it explicitly.
GimpTool sets tool->drawable in its default button_press()
implementation, and we potentially access it in
gimp_display_shell_initialize_tool(), so failing to clear it when
halting the tool may leave it as a dangling pointer, which can
result in a segfault when trying to initialize the tool in the
above function. In particular, this happens with the iscissors
tool.
Never add an alpha channel in gimp_layer_real_transform(), it was only
added because our pre-GEGL transform code was shit. This is quite the
opposite of what the bug reporter asked for, but IMO the more correct
fix.
This will NOT go to 2.10 because it changes behavior.
In GimpCanvasTransformPreview, when the image mask is not empty,
make sure to align it with the drawable using a gegl:translate
node, before combining both at the gegl:opacity node. Otherwise,
the mask is applied at the wrong offset when the drawable's offset
is not (0, 0).
Make gimp_free_select_tool_halt() protected, and call it in
gimp_foreground_select_tool_set_trimap(), so that the free-select
subobject of the foreground-select tool is properly shut down
before switching to trimap mode. In particular, this clears the
free-select tool widget at the right point; failing to do this
leads to CRITICALs later on.
In GimpToolRectangle, call gimp_tool_rectangle_update_options()
when the "[xy][12]" properties change, so that the "x", "y",
"width", and "height" properties are updated accordingly.
In particular, we set these properties when committing an empty
rectangle select tool, to init the rectangle to the current
selection bounds, and this call is necessary so that the "x", "y",
"width", and "height" tool options are properly updated as well.
Make the bottom-left entry request only minimal width, it expands
anyway. Also replace the "Columns:" label by a "grid" icon and set a
tooltip on the columns spinbutton.
In gimp_image_set_colormap(), make sure the image's colormap always
has at least one color -- babl palette formats must have at least
one color.
If the function is called with 0 colors, use black. We still need
to support this case, in particular, since existing XCFs may have
an empty colormap, and since plug-ins can call
gimp-image-set-colormap with 0 colors.