Pass through mode uses the same compositing logic as REPLACE mode,
however, it's a special case of REPLACE, where the layer is already
composited against the backdrop. This allows us to take a few
shortcuts that aren't generally applicable to REPLACE mode.
Add a dedicated op class for pass through mode, derived from the
REPLACE mode op, implementing these shortcuts.
and add gimp_drawable_invert_linear(). Also, finally deprecate
gimp_invert() and port all its uses in plug-ins and scripts to
gimp_drawable_invert_non_linear() so the result is the same.
Fix the default brush name -- "Round Fuzzy" was gone for a while :P
The fact that "Hardness 050" was the default brush regardless is a
conincidence; see the bug report for more details.
glib-genmarshal was rewritten in glib 2.53.4, and as of now (2.53.6)
it has a bug where it unconditionally generates marshaler bodies,
even for standard marshalers, even with --stdinc. This causes
libgimpwidgets to define and export g_cclosure_marshal_VOID__INT()
and g_cclosure_marshal_VOID__OBJECT(), which upsets defcheck, and
breaks the build.
Work around this for now by using --header --body when generating
the marshal.c files, which includes the prototypes in the source,
instead of including the header ourselves. This is the only code
path where the new glib-genmarshal doesn't generate bodies for
standard marshalers. Note, however, that this usage is deprecated,
so we'll probably want to change it back once it's fixed.
...in both the core and libgimp.
Images now know what the default mode for new layers is:
- NORMAL for empty images
- NORMAL for images with any non-legacy layer
- NORMAL_LEGAVY for images with only legacy layers
This changes behavior when layers are created from the UI, but *also*
when created by plug-ins (yes there is a compat issue here):
- Most (all?) single-layer file importers now create NORMAL layers
- Screenshot, Webpage etc also create NORMAL layers
Scripts that create images from scratch (logos etc) should not be
affected because they usually have NORMAL_LEGACY hardcoded.
3rd party plug-ins and scripts will also behave old-style unless they
get ported to gimp_image_get_default_new_layer_mode().
gimp_layer_update_mode_node(): when showing the mask, set mode to
NORMAL, and make sure that the composite space is PERCEPTUAL for
LEGACY layers, and LINEAR (or whatever is chosen in layer attibutes)
otherwise.
this commit changes just those which make no difference to
functionality: property and object member defaults that get overridden
anyway, return values of g_return_val_if_fail(), some other stuff.
Looked a bit deeper into heal: while I didn't try to understand what
it's actually doing, this is strange: there is a comment that says
that healing should done in perceptual space, and the code uses
R'G'B'A float (at least it completely did before the last commit).
On the other hand, the code adds and subtracts temporary buffers,
which screams "gamma artifacts" unless done in linear space.
This commit changes everything to use linear float buffers,
and removes the comment. It "looks" right to me now, please test.
Split libappgegl into libappgegl-generic and libappgegl-sse2, and
move the SSE2 code (part of the newly added smudge code) to the
latter, so that the rest of the code can be compiled without SSE2
compiler flags. This allows building GIMP with SSE acceleration
enabled, while running the resulting binary on a target with no
SSE accelration.
Commit 3635cf04ab moved the special
handling of bottom-layer compositing to GimpOperationLayerMode.
This required giving the op more control over the process()
function of its subclasses. As a temporary workaround, the commit
bypassed the subclasses entirely, using "gimp:layer-mode" for all
modes. This is the reckoning :)
Add a process() virtual function to GimpOperationLayerMode, which
its subclasses should override instead of
GeglOperationPointComposer3's process() functions. Reinstate the
subclasses (by returning the correct op in
gimp_layer_mode_get_oepration()), and have them override this
function.
Improve the way gimp_operation_layer_mode_process() dispatches to
the actual process function, to slightly lower its overhead and
fix some thread-safety issues.
Remove the "function" field of the layer-mode info array, and have
gimp_layer_mode_get_function() return the
GimpOperationLayerMode::process() function of the corresponding
op's class (caching the result, to keep it cheap.) This reduces
redundancy, allows us to make the ops' process() functions private,
and simplifies SSE dispatching (only used by NORMAL mode,
currently.)
Move the blend and composite functions of the non-specialized
layer modes to gimpoperationlayermode-{blend,composite}.[hc],
respectively, to improve code organization.
Move the SSE2 composite functions to a separate file, so that they
can be built as part of libapplayermodes_sse2, allowing
libapplayermodes to be built without SSE2 compiler flags. This
allows building GIMP with SSE acceleration enabled, while running
the resulting binary on a target with no SSE accelration.
Add a "blend_function" field to the layer-mode info array, and use
it to specify the blend function for the non-specialized modes.
This replaces the separate switch() statement that we used
previously.
Remove the "affected_region" field of the layer-mode info array.
We don't need it anymore, since we can go back to using
GimpOperationLayerMode's virtual get_affected_region() function.
Last but not least, a bunch of code cleanups and consistency
adjustments.
Paint tools in straight line mode (shift click) will now display the
angle in status bar. Angle 0 is considered as the horizontal line from
left to right, and angle is measured counterclockwise from there, which
is the most common convention.
Use GIMP_LAYER_MODE_NORMAL (not NORMAL_LEGACY) when falling back from
gimp_paint_core_replace() to gimp_paint_core_paste() for layers
without alpha. Adapt the format of the used paint buffers accordingly.
... and fix flatten-image along the way. *And* do some cleanup.
Currently, gimp_image_merge_layers() combines the layers on its own,
one by one. This is incompatible with pass-through groups, because
the group's buffer is rendered independently of its backdrop, while
we need to take the backdrop into account when mergeing the group.
Instead, render the subgraph of the parent graph, corresponding to
the set of merged layers, directly into the new layer. Since the
layers we merge are always visible and continuous, all we need is a
minor massage to the parent graph to make it work. This takes care
of pass-through groups intrinsicly.
This commit also changes the behavior of flatten-image: Currently,
the flattened layers are rendered directly on top of the opaque
background, which can make previously-hidden areas (due to layers
using composite modes other than src-over, or legacy layer modes)
visible. This is almost certainly not desirable.
Instead, construct the graph such that the flattened layers are
combined with the background only after being merged with one
another.
GimpFilter's is_last_node field only reflects the item's position
within the parent stack. When a layer is contained in a pass-
through group, it can be the last layer of the group, while not
being the last layer in the graph as a whole (paticularly, if
there are visible layers below the group). In fact, when we have
nested pass-through groups, whether or not a layer is the last
node depends on which group we're considering as the root (since
we exclude the backdrop from the group's projection, resulting in
different graphs for different groups).
Instead of rolling our own graph traversal, just move the relevant
logic to GimpOperationLayerMode, and let GEGL do the work for us.
At processing time, we can tell if we're the last node by checking
if we have any input.
For this to work, GimpOperationLayerMode's process() function needs
to have control over what's going on. Replace the derived op
classes, which override process(), with a call to the layer mode's
function (as per gimp_layer_mode_get_function()) in
GimpOperationLayerMode's process() function. (Well, actually, this
commit keeps the ops around, and just hacks around them in
gimp_layer_mode_get_operation(), because laziness :P)
Keep using the layer's is_last_node property to do the invalidation.
GimpTileHandlerProjectable is similar to GimpTileHandlerValidate,
except that it calls {begin,end}_render() on its associated
projectable before validating.
In pass-through mode, the group layer-stack's input is connected to
the backdrop. However, when rendering the group's projection, we
want to render the stack independently of the backdrop.
Unfortunately, we can't use the stack's graph as a subgraph of two
different graphs.
To work around that, the next few commits add a mechanism for a
projectable to be notified before and after its graph is being
rendered. We use this mechanism to disconnect the stack's graph
from the backdrop before rendering the projection, and reconnect
it afterwards. Yep, it's ugly, but it's better than having to
maintain n copies of (each node of) the graph (each nesting level
requires an extra copy.)
This commit adds {begin,end}_render() functions to GimpProjectable.
These functions should be called right before/after rendering the
projectable's graph.
When any of the children of a pass-through group excludes its
backdrop, the group itself should exclude the backdrop too. Override
get_excludes_backdrop() to follow this logic, and call
update_excludes_backdrop() when this condition might change.
Note that we always composite pass-through groups using src-over mode,
so to actually hide the backdrop, we need to disconnect it from the
group's mode node's input pad (and reconnect it, when the backdrop is
no longer hidden).
Override GimpDrawable::get_source_node() for GimpGroupLayer. Use
a node that contains both the drawable's buffer-source node, and the
layer stack's graph node. Choose which one of these to connect to
the source node's output based on the group's layer mode: the stack
graph for pass-through mode, and the buffer-source node for all the
rest.
When in pass-through mode, connect the source node's input (which
receives the backdrop) to the stack graph's input. Keep maintaining
the projection in pass-through mode. ATM, the projection uses the
same graph as the source node, so it's rendered against the group's
backdrop -- we don't want that. The next few commits fix it.
Update the group's drawable directly upon filter stack update in
pass-though mode, because the group's graph doesn't go through the
projection.
TODO: if any of the group's children (or a child of a nested pass-
through group, etc.) uses dst-atop/src-in, this needs special
attention.
Make sure the input of the layer's filter node is connected to its
source node (when it has an input pad), so that, once we implement
pass-though mode, the group's source node can see the backdrop.
For pass-through groups, we want to use the group's layer-stack
graph directly in its filter node, in place of the drawable's
buffer-source node. Add a get_source_node() vfunc to GimpDrawable,
which defaults to returning the buffer-source node, and use it in
gimp_drawable_get_source_node() instead of using the buffer-source
node directly. We'll later override this function for
GimpGroupLayer.
... causing compilation to fail on 32 bit targets
Use SSE2 compiler flags when building libappgegl, since it's used by
the new smudge tool code.
Avoid using SSE for the smudge tool if SSE acceleration is disabled
at runtime, or if the buffers are not properly aligned.
Add "gboolean with_filters" to gimp_drawable_calculate_histogram(),
which is passed as FALSE in almost all places, except the histogram
dockable where we want to see both the drawable's unmodified histogram
*and* the histogram after filters are applied.
During constrained motion, round the slider value before clamping
it, so that the slider limits are always enforced. Additionally,
snap the slider to 1/12-ths of the line length, rather than
1/24-ths.
Make sure that sliders can never have negative-zero values, which
can result in a -inf base for spiral.
Shift-click should actually toggle only within a given group. The new
capability of toggling only a sub-item, brought by commit 970e9ac is
still feasible in 2 steps: first toggling the parent (item group), then
the desired child.
It brings now a third possibility with exclusive toggle among many
children items, without touching other groups and top-level items.
... so that when the base and balance sliders overlap, the base
slider is the one that's picked, since the balance slider is
constrained by the base, but not the other way around.
When loading tiles from an XCF, reject tiles whose on-disk size is
greater than 1.5 times the size of an uncompressed tile -- a limit
that is already present for the last tile in the buffer. This
should allow for the possibility of negative compression, while
restricting placing a realistic limit.
Currently, no limit is placed on the on-disk tile data size. When
loading RLE- and zlib-compressed tiles, a buffer large enough to
hold the entire on-disk tile data, up to 2GB, is allocated on the
stack, and the data is read into it. If the file is smaller than
the reported tile data size, the area of the buffer past the end
of the file is not touched. This allows a malicious XCF to write
up to 2GB of arbitrary data, at an arbitrary offset, up to 2GB,
below the stack.
Note that a similar issue had existed for earlier versions of GIMP
(see commit d7a9e6079d), however,
since prior to 2.9 the tile data buffer was allocated on the heap,
the potential risk is far smaller.
...with known plugins
Add new plug-in file-raw-placeholder.c that registers itself for
loading all RAW formats from file-raw/file-formats.h, but does nothing
except returning an error message pointing to darktable and
RawTherapee.
When no real RAW loader is installed, this plug-in is selected
automatically as RAW loader, otherwise the first installed RAW loader
is used. Selecting another in prefs still works as before.
... "threads" property.
Actually there is no need of having a public GEGL_MAX_THREADS as written
in the previous commit. We can just retrieve the max for a GObject
property.
Raise GIMP_MAX_NUM_THREADS to 64, following the changes in GEGL (see
GEGL commits 6d128ac and f26acbb). This is still considered unstable and
to be used at one's own risk (cf. GIMP commit 1f5739d) but at least, it
could allow discovering and fixing bugs.
It would be nice if GEGL_MAX_THREADS could be public so that to not have
to edit this by hand at each change.
- trailing whitespaces cleaned out;
- vectors are called "path" in all visible strings;
- do not check for floating selection and active channel: oppositely to
layers, a vector can be selected in the same time as a channel, and
while there is a floating selection.
and update the grid as soon as a constraint is changed, not only on
the next motion. Change GimpTransformTool to forward the events to the
widget if it exists, but still handle them if it doesn't (yes this
code duplication is ugly, but the widget can hardly handle events if
it doesn't exist...).
More than 2000 lines of code less in app/, instead of
if (instance->member)
{
g_object_unref/g_free/g_whatever (instance->member);
instance->member = NULL;
}
we now simply use
g_clear_object/pointer (&instance->member);
Change the text selection to draw an outline around each selected
glyph. It looks just as ugly as before but at least keeps the text
readable regardless of its color.
if did revert to the previous selection and thus break stuff like
enbaling quick mask or inverting the selection, because I merged the
undo magic it does into gimp_rectangle_select_tool_halt(), whereas
before it was done by the former gimp_rectangle_tool_cancel(), so only
on explicit cancel not HALT from whatever source.
Do the same in the new code and move the undo magic from halt() to
rectangle_response(CANCEL), which is exactly the same distinction as
with the old GimpRectangleTool code.
When the user provides a filename without an extension in the save
dialog, we add one for them, update the filename in the dialog, and
retry. However, the updated filename is made up of only the
basename, leaving out the dirname part, if specified. This means
that if the user enters "/somedir/somefile", the new filename
becomes "somefile.xcf", which refers to the current directory,
instead of "somedir".
Fix this by maintaining the dirname when adding a file extension.
Don't unconditionally call COMMIT in rectangle_response(), because
that now implicitly HALTs the tool. Instead, check if we got here
because of a click, and call our commit() directly.
which is the same as g_object_class_list_properties() but filters
out the properties for which we don't want to create a GUI.
Use it in gimp_prop_gui_new().
GimpOperationTool's aux inputs were not properly ported to the new way
filter tools work (complete destruction and re-creation of the tool
dialog).
Split creating the operation GUI and adding it to the dialog into
separate functions, and call them at the right places.
Call HALT generically in gimp_tool_control() after calling COMMIT, and
remove all hacks in tools that call both COMMIT and HALT or call
halt() from commit().
Some tools interact with their subclasses (e.g. filter tool and
operation tool), and it's essential that COMMIT runs through the
entire class hierarchy before HALT.
Probably breaks something, please test.
Need to keep around the operation's name and its description, so
everything can be re-created when an image is clicked.
Instead, completely shut down GimpOperationTool when GimpGeglTool is
halted, so the next click will bring up a dialog with only the
operation selection combo.
which allow to override stuff from GimpToolInfo for dynamic tools like
GimpFilterTool and friends. When NULL, the getters are falling back to
GimpToolInfo strings.
...and "export_dialog_title"
It's ridiculous to keep this code around for strings that are only
marginally different (and not better) than the strings we can generate
from other strings we have anyway.
to ::can_undo() and ::can_redo(). They still return description
strings and the new naming is slightly off :) but get_undo_desc() will
be needed for something else soon, and half of the time the functions
are indeed used to check whether there are undo/redo staps at all.
Switch the gegl:spiral prop gui from using a line controller to
a slider-line controller, and use sliders to control the "balance"
and "base" properties.
Add supprt for placing sliders on a GimpToolLine -- handles that can
be dragged over the line. The sliders are accesible through a new
"sliders" property, and via the gimp_tool_line_{get,set}_sliders()
functions.
Add a slider-line controller, which works like a line controller,
but whose callback also supplies/takes an array of sliders.
Note that the data type for individual sliders is called
GimpControllerSlider (in particular, it's not line specific), so
that we may use it with other controller/tool-widget types in the
future.
Use the new gimp_filter_tool_get_drawable_area() instead of always
using gimp_item_mask_intersect() which is only right when the
operation is applied to the "selection" region. Also call
gimp_operation_tool_sync_op() when the region is changed in the UI.
Don't offset the ui range of op properties that use pixel-distance
units to the top-left corner of the region, since they're relative;
only do that for pixel-coordinate units. Let their ui range be
[0, width/height].
Pass a "GimpCreateControllerFunc" to all gimppropgui-*.[ch]
constructors which takes a callback (to update the config object when
the on-canvas GUI) and a controller type that determines the
callback's signature, and returns another callback (to update the
on-canvas GUI when the config object changes).
In GimpOperationTool, pass such a GimpCreateControllerFunc that
handles creating and adding on-canvas controller via the new
gimpfiltertool-widgets.[ch]. So far, a simple line like in the
blend tool is supported.
Add a custom GUI for gegl:spiral, and have its origin, radius and
angle controlled by such a line.
which return's the used drawable's offsets and a GeglRectangle
where the filter is applied according to GimpFilterOptions::region
(either the selection or the whole drawable).
and call it from GimpFilterTool's "notify" callback. Remove signal
connections from all subblasses and instead implement ::config_notify().
The config object belongs to GimpFilterTool, and only GimpFilterTool
should know when it's created and can be connected to.
so things from the tool's previous use get destroyed, including their
(maybe dangling) signal connections. Also shut down more stuff in
halt(), including destroying not just hiding the GUI.
because widgets are bound to one GimpDisplayShell. Also, chain up
unconditionally in gimp_color_tool_draw(), we always want to draw the
widget even while picking colors.
The widget is fed events by GimpFilterTool, the actual interaction
with the filters operation and config will be done by subclasses.
The order of precedence when there are multiple possible canvas
interactions is: moving the split preview guide, color picking,
widget.
- introduce new state "boolean is_first" which tracks if the currently
drawn rectangle is the first with this instance
- cancel the widget if there was no movement when creating the first
rectangle
- undo to the previous rectangle if the user created a zero-extent
rectangle
- also undo to the previous rectangle if a newly drawn rectangle is
canceled with button-3 release
We can't rely on g_object_unref() in halt() for breaking all property
GBindings between the tool options and GimpToolRectangle, because we
might be in the middle of a signal emission which also refs and keeps
the rectangle alive until the last callback returns. So we had
dangling rectangles interacting with tool options.
Remember all bindings in a list, and break them explicitly when we
shut down the rectangle in halt().
Also, forgot to unset the display's highlight in the rectangle
selection tool.
because of bailing out early after emitting "response". Instead, don't
ref the object around this function, and move the "response" emission
to the end of the function.
The tool manager still keeps an active tool which it unrefs on
destruction, triggering a final HALT on the tool, which may want to
lookup tool options to reset something. Happened with the new
widget-ported rectangle select tool.