Apart from being less code, this actually gives us a nice performance
improvement. Up until a few years ago, if you pass `NULL` as the
marshaller for a signal, GLib would fall back to
`g_cclosure_marshal_generic` which uses libffi to pack/unpack its
arguments. One could avoid this by specifying a more specific
marshaller which would then be used to immediately pack and unpack into
GValues with the correct type.
Lately however, as a way of optimizing signal emission (which can be
quite expensive), GLib added a possibility to set a va_marshaller, which
skips the unnecessary GValue packing and unpacking and just uses a
valist variant.
Since the performance difference is big enough, if the marshaller
argument is NULL, `g_signal_new()` will now check for the simple
marshallers (return type NONE and a single argument) and set both the
generic and the valist marshaller. In other words, less code for us with
bigger optimizations.
In case you also want va_marshallers for more complex signals, you can
use `g_signal_set_va_marshaller()`.
In gimp_palette_mru_add(), if the added color doesn't match an
existing color, don't look for two duplicate existing colors (which
has quadratic complexity), since there shouldn't be any under
normal circumstances (as we're not adding duplicates to begin
with).
for procedure arguments. This implies creating a new value array in
gimp_procedure_run() if the passed array is too short, instead of
just appending to the passed array, which was ugly anyway.
Which return proper GParamSpecs. Incuding some useless testing code in
gimp_procedural_db_proc_info(), to make sure things work, will go away
again soon.
Also generate comments like "Must be freed with g_free()" for all
return values instead of manually and inconsistently having them on
some return values only.
allow to pass booleans and enums to GIMP_TYPE_INT32 arguments. We
didn't have them in the old plug-in API, and being less strict allows
for booleans and enums being passed to old procedures transparently.
`g_object_notify()` actually takes a global lock to look up the property
by its name, which means there is a performance hit (albeit tiny) every
time this function is called. For this reason, always try to use
`g_object_notify_by_pspec()` instead.
Basically this commit makes sure that all return values that are marked
as "Returns:" also have a `(nullable)` annotation if it is mentioned on
the same line that NULL can also be returned.
This will prevent a few problems in GObject-introspection.
Documentation-wise in C, this doesn't matter a lot, but it allows
GObject-Introspection based bindings to use their built-in versions when
they want to render any kind of documentation (for example, docs for
Python plugins can render `%NULL` as `None`).
In GimpPaintbrush, calculate grad_point in
_gimp_paintbrush_motion() independently of any stroke, and pass
its value down to GimpPaintbrush::get_paint_params() at each
stroke, so that the color is the same for all strokes, even when
using a color/random dynamics.
In GimpFilterTool, add a new "clipping" option, which can be either
Adjust or Clip. This option is controllable for layers, when
there's no selection mask.
When set to Adjust, which is used by default, the filter's output
may extent past the drawable's boundary, and the drawable will be
resized to match the output when committed.
In GimpDrawableFilter, add a new gimp_drawable_filter_set_clip()
function, which controls whether the filter clips the result to the
drawable's boundary, or extends it past it. The latter is only
possible for layers, when the selection mask is empty.
Add a new "clip" parameter to gimp_drawable_merge_filter(). When
set to FALSE, the function resizes the drawable to the bounding box
of the filter's output, instead of clipping the output to the
drawable's boundary.
In gimp_gegl_apply_cached_operation(), copy the cached results to
the destination buffer *before* reconnecting the operation node, as
this may cause the cache to be cleared (see commit
gegl@4347255cd9d5031e6757c70fdde5c63539d5f508).
In GimpGroupLayer, when recalculating the group's size as a result
of a change to one of the child layers (now including in response
to a child layer's GimpDrawable::bounding-box-changed signal),
calculate the group's bounding box (the bounding box of all its
child layers' bounding boxes) alongside its logical bounds. Like
in GimpLayer, use the logical bounds as the bounding box if the
group has a mask.
This bounding box is passed to the group's projection, via
GimpGroupLayer's GimpProjectable::get_bounding_box()
implementation, resulting in a buffer whose extent is the same as
the bounding box.
In GimpProjectable, replace gimp_projectable_get_size(), which only
returned a width and a height, with
gimp_projectable_get_bounding_box(), which returns a full
rectangle. This allows projectables to have an arbitrary bounding
box, not limited to a (0, 0) top-left corner.
Adapt GimpProjection, creating a buffer with corresponding extent
to the projectable's bounding box.
Adapt GimpImage and GimpGroupLayer.
Implement GimpDrawable::get_bounding_box() for GimpLayer, by
returning the bounding box of its source node. If the layer has a
mask, we simply return its logical boundary, since the layer can't
extend past the mask.
Maintain the bounding box of drawables (i.e., the bounds of their
actual rendered content) separately from their logical boundary (as
shown in the UI).
The bounding box is calculated through the new
GimpDrawable::get_bounding_box() virtual function, which has a
corresponding gimp_drawable_get_bounding_box() function; the
default implementation simply returns the drawable's logical
boundary. The bounding box is specified in drawable coordinates,
i.e., it's not affected by the drawable's offset.
The bounding box is recalculated through
gimp_drawable_update_bounding_box(), which should be called
whenever a change may affect the bounding box (for example, when
setting a new buffer, as done implicitly by GimpDrawable's
::set_buffer() implementation, or when a drawable filter's
properties change, as will be done by GimpDrawableFilter in a
following commit). When the bounding box changes, the affected
regions of the drawable are updated, and the
GimpDrawable::bounding-box-changed signal is emitted.
When gimp_drawable_update() is called with negative width/height
values, the entire drawable's bounding box is updated, rather than
only its logical boundary.
Likewise, GimpDrawableStack and GimpLayerStack are adapted to use
the bounding box, instead of the logical bounds, when updating the
drawable's area.
In GimpDrawable::set_buffer(), and the corresponding
gimp_drawable_set_buffer_full() function, take a bounds rectangle,
which specifies both the drawable's new offset and its new size,
instead of only taking the new offset. In
gimp_drawable_real_set_buffer(), set the item size according to the
rect dimensions, instead of the buffer dimensions. The rect's
width/height may be 0, in which case the buffer's dimensions are
used.
Adapt the rest of the code.
We do this in preparation for maintaining the drawable's bounding
box separately from its logical bounds, allowing the drawable
content to extend beyond its bounds.
This is especially true since procedures are defined by plug-ins,
possibly third-party. So they may not have a first run mode parameter.
Also we don't want plug-ins to generate CRITICALs in the core
application.
Therefore before trying to get and/or set the run mode parameter, always
check that this first parameter exists, and if so, is it a GimpRunMode
enum?
Fixes similar errors:
> GIMP-CRITICAL: gimp_value_array_index: assertion 'index < value_array->n_values' failed
Moreover it also makes sense to allow plug-ins to not have such
parameter. What if you make a plug-in which always work in a single mode
and don't ever plan/want to make it work interactively for instance?
Lastly old Python 2 plug-ins didn't even have a run mode parameter (I
guess our old Python 2 wrapper was getting rid of it?).
which means that it's now included normally via gimpbase.h
and not any longer via gimpbasetypes.h which we only did out
of lazyness. A *lot* of files in libgimp* and app/ now need to
... as a drop-in replacement for gegl_buffer_dup(), which COWs all
tiles of the source buffer, including ones that aren't fully
included within buffer's extent.
In gimp_gegl_buffer_copy(), assume that the area outside the abyss
is empty, and so, when the abyss policy is NONE, allow data otuside
the source/destination abyss to be copied, by temporarily modifying
the abyss. This allows tiles that aren't fully contained within
the abyss to be COWed, unlike gegl_buffer_copy(), which has to be
more general.
The latter is broken and doesn't guarantee a decimal point with the
current bug. Also, g_ascii_dtostr() doesn't need the format parameter
and produces nicer output.
In particular, if the shebang is `#!/usr/bin/env lang` and we have not
registered a specific interpreter for `lang`, the system should leave
the env tool search the right interpreter for us. We only bypass env
when we set our own explicit interpreter.
Apply this to palette-to-gradient.py plug-in.
- libgimpbase: change GPParam to transfer all information about the
GValues we use, in the same way done for GPParamDef. GPParam is now
different from GimpParam from libgimp, pointers can't be casted any
longer. The protocol is now completely GimpPDBArgType-free. Remove
gp_params_destroy() from the public API.
- libgimp: add API to convert between an array of GPParams and
GimpValueArray, the latter is now the new official API for dealing
with procedure arguments and return values, GimpParam is cruft (the
wire now talks with GimpPlugIn more directly than with the members
of GimpPlugInInfo, which need additional compat conversions).
- libgimp, app: rename gimpgpparamspecs.[ch] to simply
gimpgpparams.[ch] which is also more accurate because they now
contain GValue functions too. The code that used to live in
app/plug-in/plug-in-params.h is now completely in libgimp.
- app: contains no protocol compat code any longer, the only place
that uses GimpPDBArgType is the PDB query procedure implementation,
which also needs to change.
- app: change some forgotten int32 run-modes to enums.
- Change the wire protocol's GPProcInstall to transmit the entire
information needed for constructing all GParamSpecs we use, don't
use GimpPDBArgType in GPProcInstall but an enum private to the wire
protocol plus the GParamSpec's GType name. Bump the wire protocol
version.
- Add gimpgpparamspecs.[ch] in both app/plug-in/ and libgimp/ which
take care of converting between GPParamDef and GParamSpec. They
share code as far as possible.
- Change pluginrc writing and parsing to re-use GPParamDef and the
utility functions from gimpgpparamspecs.
- Remove gimp_pdb_compat_param_spec() from app/pdb/gimp-pdb-compat.[ch],
the entire core uses proper GParamSpecs from the wire protocol now,
the whole file will follow down the drain once we use a GValue
representation on the wire too.
- In gimp_plug_in_handle_proc_install(), change the "run-mode"
parameter to a GParamSpecEnum(GIMP_TYPE_RUN_MODE) (if it is not
already an enum). and change all places in app/ to treat it as an
enum value.
- plug-ins: fix cml-explorer to register correctly, a typo in
"run-mode" was never noticed until now.
- Add gimpgpcompat.[ch] in libgimp to deal with all the transforms
between old-style wire communication and using GParamSpec and
GValue, it contains some functions that are subject to change or
even removal in the next steps.
- Change the libgimp GimpProcedure and GimpPlugIn in many ways to be
able to actually install procedures the new way.
- plug-ins: change goat-exercise to completely use the new GimpPlugIn
and GimpProcedure API, look here to see how plug-ins will look in
the future, of course subject to change until this is finished.
- Next: changing GPParam to transmit all information about a GValue.
In gimp_gegl_apply_[cached_]operation(), use a longer iteration
interval (resulying in bigger chunks) when processing the op, than
the iteration interval used for rendering the projection. In
particular, use an even longer interval when processing area
filters, since their may be particularly sensitive to the chunk
size (see, for example, issue #3711). Likewise, use the asme
longer interval when not showing progress indication, since we
don't need to stay responsive in this case (but don't avoid
chunking altogether, to reduce the space required for intermediate
results).
This allows us to process an op faster when committing a filter,
while still remaining responsive (if overall slower) during
preview.
... which determines if a node is an area filter operation. If the
node is a meta op, we conservatively return TRUE, as it may involve
an area-filter op.
all the stuff from app/core/gimpparamspecs.[ch] that is not about
image, drawable etc IDs, these will have to go to libgimp with
different implementations than in app/.
Add an assigned-threads variable to the dashboard's misc group,
showing the number of worker threads which currently have work
assigned. See commit
gegl@fc532f06773bd292c86abee31ac18f62e26d39df.
When translating a layer group, avoid separately updating the
original area of the child layers before translating them (as per
the fix to issue #3484), as this results in quadratic time
complexity w.r.t. to the maximal subgroup nesting level. Instead,
simply defer the updating of the group's offset until *after*
translating the child layers, so that their original area isn't
clipped by the parent, while their new area is still properly
updated even if the parent's size changes (see comment in code).
There are no replacements. Just we must make sure that all GTK+/GDK
calls are run from the main thread, which is already what we were doing.
Actually I don't even think these were doing anything as we were not
calling gdk_threads_init() so the default lock functions were not set
anyway. These were just bogus calls.
Since introducing gimp_display_shell_canvas_tick() we were setting
shell->disp_width and shell->disp_height in the tick callback, which
was too late and caused redundant rendering and scrolling, but was
never noticed.
Now we clear the render cache and its valid region in
gimp_display_shell_canvas_size_allocate() directly and set
shell->disp_width and shell->disp_height immediately, so other places
that listen to the canvas' size-allocate get the right values.
The old size of the canvas gets to the tick callback using a small
struct as user data.
Add an active-thread variable to the dashboard's misc group,
showing the number of active worker threads. See commit
gegl@6a3a6314d4d4cd668e0f6164afc0fde8b9c7c001.
In gimp:gradient, fix dithering to correspond to how we actually
round float values to 8-bit. In particular, this avoids
introducing noise when a component is fixed at 0 or 1 along a
segment.
Revert the previous commit 786686a541 and comes up with a better fix.
Let's actually change the image base type and add its colormap as close
as possible without any GUI calls in-between. I also add an explicative
comment so that people are aware of this call proximity requirement to
avoid future problems when the code gets remixed.
It should be better than hacking around with exception in GUI code, and
should (hopefully) avoid other similar bugs.
Fixes the GIMP-CRITICAL:
> gimp_babl_format_get_trc: assertion 'format != NULL' failed".
During an indexed conversion, we are in a weird limbo state between the
time we changed the base-type to GIMP_INDEXED and when we actually set
the new palette. If during this time, we hit a context switch (which
typically happens during GUI code; in this specific cases, the various
progress updates would call gimp_widget_flush_expose() which does
trigger context switch), then gimp_display_shell_update_title_idle()
might be idly called. And when it does, it sees an indexed function with
neither format nor builtin profile.
Must initialize shell->render_buf_width,height before realize(), so
move the code to gimp_display_shell_init(), it doesn't depend on the
shell being realized.
When set to FAST we do nearest neighbor from the next bigger mipmap
level instead of linear or box filtering - this gives a slight and
permanent boost to painting, and all updates, having one that combines
best of both worlds and reblits in high quality after a timeout would
be even more desirable.
Introduce a render cache that keeps the result of scaling, color
management, display filters and shell mask (for tools like fuzzy
select).
Change gimpdisplayshell-render.[ch] to only render to the cache and
manage a cairo region of the cache's valid area. Call cache
invalidation functions form various places. Change the API of all
render functions to be in display coordinates.
Also get rid of gimpdisplayxfer.[ch] because we now have a
canvas-sized cairo surface which is a surface similar to the
destination surface.
Not for 2.10 because the GEGL op's result looks different. I have no
clue how softglow is supposed to look at all, but the removed plug-in
did things that can't possibly be done with physical light in a
darkroom, so chances are the GEGL op is more correct.