Commit graph

24904 commits

Author SHA1 Message Date
Alx Sa
1beb1f7464 core: Prevent crash when multiple channels are copied
Currently, we do not handle copying multiple channels.
The function call returns NULL but does not set an error
message - this causes a crash when we try to print out
error->message.
This patch changes the g_return_val_if_fail () call to an
IF statement, so that we can initialize the error object
with a message to prevent the crash.
2025-01-08 02:01:28 +00:00
Jehan
4e57e7b4ea Issue #12641: Help>About GIMP dialog does not show an update in 3.0 RC1.
RC numbering was not taken into account for version comparison.
2025-01-07 17:10:57 +01:00
Jehan
33ab56f554 Issue #12640: crash on font not found.
pango_context_load_font() can return NULL.
2025-01-07 12:57:44 +01:00
Thomas Manni
c434fe8be8 Issue #12411: manage filters undo when deleting/cutting a layer
Push a filter remove undo for each filter when their layer is deleted or cut.
2025-01-07 06:01:09 +00:00
Alx Sa
5e3047c70d core: Apply noninteractive filters direct to drawable
When the user checks "Merge Filters" on interactive filters,
we move the filter to the bottom of the stack so it applies
directly onto the drawable (instead of merging the entire filter
stack).
However, we did not do this reordering for non-interactive filters,
resulting in incorrect output. This patch applies the same logic
so that when you apply a filter like Invert, it affects the drawable
only and does not cause problems for any existing NDE filters.
2025-01-04 19:04:11 +00:00
Lukas Oberhuber
65d4ec7ea4 macos: Open files from finder fixing #12603 #12465 #12439
Needs to be tested on MacOS below 15 (Sonoma)
2025-01-04 18:23:50 +00:00
Daniele Forsi
994137a82e Fix spelling errors found with codespell 2025-01-04 15:11:03 +00:00
Alx Sa
86861952aa dialogs: Add Help buttons to About/Tips/Welcome dialogs
The About, Tips, and Welcome dialogues had Help IDs created,
but they were not being used on the dialogues themselves. This
meant there was no help button, and pressing F1 did not pull up
the help manual when pressed.
To be consistent, the Help ID was added to the gimp_dialog_new ()
calls for Tips and Welcome. Since About is not based on GimpDialog,
this patch adds it manually (after checking to make sure the user
has enabled help buttons in Preferences).
This patch also constrains the Tip dialogue's initial width to match
the About and Welcome dialogues.
2025-01-04 02:11:18 +00:00
Alx Sa
0243cf5c08 dialogs: Show icons in Tips Dialog on Windows
The Previous and Next buttons have arrow icons. However, these
have not shown up on Windows since at least 2.10, as buttons
created with gtk_dialog_add_button () don't automatically make
their icons visible on all platforms.
This patch uses the existing "image" variable to store the icon
rather than passing it inline in gtk_button_set_image ().
This allows us to explicitly set it to be visible so it shows for
all users.
2025-01-03 02:50:21 +00:00
Øyvind Kolås
e7149ab200 build,app: depend on gegl-0.4.52 2024-12-27 10:25:42 +01:00
Alx Sa
201ab6ff7a actions: Add new Navigation dockable icons
This patch connects the new icons made by Denis Rangelov
for the Shrink Wrap and Fill Window zoom actions in the
Navigation Dockable.
2024-12-26 17:00:17 +00:00
Jehan
bfe842cd1d Issue #12359: encoding conversion undo does not undo the mask conversion.
In particular, we could end up with mask of wrong bytes per pixel, which
was what was happening in the report.

This commit adds a new GimpDrawableFilterMask class because we needed to
implement a specific is_attached() method for effect masks.

Note: the file is added to POTFILES but the only localized string
already existed elsewhere. So this doesn't break string freeze.
2024-12-26 15:55:05 +01:00
Andre Klapper
a6fdc51178 Correct help ID for GEGL graph dialog
Fix the mismatch between the ID in gimp-help and the ID in gimp.

Followup to c3bd2d368a

Fixes #12613
2024-12-26 12:51:29 +01:00
Jehan
d20cfa1124 app: salvage XCF with broken filters.
This is not a fix for #12359 yet, but an improvement to XCF loading.
When filters fail to load for any reason, we should still load the
layer. Only drop the broken filter.
2024-12-22 14:31:55 +01:00
Jehan
59d65818ce Issue #12292: image remains in memory after closing view.
I've tested lightly this removal. I don't think this code was really
needed or logical anyway, though maybe I am missing something because I
barely discover the existence of this "Image Selection" feature of dock
windows!
2024-12-22 00:19:08 +01:00
Alx Sa
aad2a5967e xcf: Don't try to deserialize an empty GimpConfig string
When loading a GimpConfig property for a filter, we assumed that
the string is always valid. This patch adds code to check if we read
in the string correctly - if we didn't (or if it was tampered with), we
don't try to deserialize an empty string.
2024-12-21 17:40:50 +00:00
Thomas Manni
c346838043 actions: Set NDE filter before initialization
Filters with custom dialogues like Curves and Levels did not
have the existing filter set before being initialized. This meant
that if you have a different layer selected, editing an existing
filter would add a new filter to your selected layer rather than
editing the filter on its own layer.
This patch sets the NDE filter in the tool before initialization,
so that it can get the correct layer to edit and update the
filter settings on that one.
2024-12-20 16:54:36 +00:00
Jehan
0a47c59a26 app: some updates regarding MR !1996.
1. After discussions on IRC, we agreed that "A new perceptual blending
   space was added in GIMP 3.0" was a nicer reason to display for
   bumping the XCF version, because instead of focusing on fixes, we
   focus on the new feature (a real "perceptual blending space" was
   added for modes) and also because naming "perceptual" in some form
   seems more helpful for people to figure out what is different.
2. Fixing sensitivity of the non-linear (formerly named perceptual)
   actions when the blend or composite spaces are mutable.
2024-12-19 23:23:50 +01:00
Øyvind Kolås
3f3b29ba12 app, libgimp, pdb: fix perceptual blend space for linear TRC ICC profiles
In previous versions what has been stored/specified as perceptual blending or
compositing spaces has really been the non-linear variant of the images babl
space.

To maintain loading of old files, the code has been updated to actually mean
non-linear and a new perceptual value has been added to the GimpLayerColorSpace
enum, while preserving all old enum values.

This change bumps XCF file version to 23
2024-12-19 23:23:50 +01:00
Alx Sa
7bcc23ec05 tools, widgets: Fix NDE restrictions when different layer selected
As noted by Thomas Manni, editing NDE filters was still affected by
restrictions on existing layers, even if the edited layer did not have
those restrictions.
This patch alters gimp_item_tree_view_effects_edited_clicked () so that
it checks if the edited filter's drawable is visible or pixel locked,
rather than the currently selected layers. It also adds checks in
GimpFilterTool tool to verify an existing filter is being edited before
preventing certain operations.
2024-12-19 17:42:43 +00:00
Anders Jonsson
70db83ee33 dialogs: remove border around toolbar icons in preferences 2024-12-18 20:48:00 +00:00
Jehan
277d49235f Issue #12576: crash when applying filter from history after an edit. 2024-12-18 13:53:52 +01:00
Jehan
aa2d35c5fc app, pdb, plug-ins: replace (plug-in-bump-map).
For plug-in writers reference, these are equivalent:

- (plug-in-bump-map RUN-NONINTERACTIVE img drawable bump-layer azimuth elevation depth 0 0 0 0 FALSE FALSE 0)
+ (let* ((filter (car (gimp-drawable-filter-new drawable "gegl:bump-map" ""))))
+   (gimp-drawable-filter-configure filter LAYER-MODE-REPLACE 1.0
+                                   "azimuth" azimuth "elevation" elevation "depth" depth
+                                   "offset-x" 0 "offset-y" 0 "waterlevel" 0.0 "ambient" 0.0
+                                   "compensate" FALSE "invert" FALSE "type" "linear"
+                                   "tiled" FALSE)
+   (gimp-drawable-filter-set-aux-input filter "aux" bump-layer)
+   (gimp-drawable-merge-filter drawable filter)
+ )

The "type" argument now uses strings.

This commit also do a big cleanup of remaining now-unused helper
functions in the compat PDB code.
2024-12-17 16:24:54 +00:00
Jehan
e2d7cc163e app, pdb, plug-ins: replace (plug-in-displace).
For plug-in writers reference, these are equivalent:

- (plug-in-displace RUN-NONINTERACTIVE work-image frame-layer
-                   x-displacement y-displacement
-                   TRUE TRUE aux1 aux2 abyss))
+ (let* ((abyss "black")
+        (filter (car (gimp-drawable-filter-new frame-layer "gegl:displace" ""))))
+
+   (if (= edge-type 1) (set! abyss "loop"))
+   (if (= edge-type 2) (set! abyss "clamp"))
+
+   (gimp-drawable-filter-configure filter LAYER-MODE-REPLACE 1.0
+                                   "amount-x" x-displacement "amount-x" y-displacement "abyss-policy" abyss
+                                   "sampler-type" "cubic" "displace-mode" "cartesian")
+   (gimp-drawable-filter-set-aux-input filter "aux" aux1)
+   (gimp-drawable-filter-set-aux-input filter "aux2" aux2)
+   (gimp-drawable-merge-filter frame-layer filter)
+ )

I also changed a test which (I think) was just a no-op since do-x and
do-y were 0 0 (hence FALSE). Therefore the whole filter processing was
ignored. Note though that unlike the rippling animation filter, I
haven't tested the test script.
2024-12-17 16:24:54 +00:00
Jehan
a6392ed84a app, libgimp, pdb, plug-ins: add a few Script-fu wrapper of libgimp filter API.
In particular (gimp-drawable-filter-configure),
(gimp-drawable-merge-filter) and (gimp-drawable-append-filter) are
proper Script-fu methods.

I had to rename the PDB procedures for the 2 latter because they were
clashing with these wrapper. I had not realized that private PDB
procedures are still visible by Script-fu. This is not so glop. :-/

Right now, it doesn't look so useful compared to the -new- one-liner
variant procedures. But it will make sense when I will add aux input C
procedure wrappers.
2024-12-17 16:24:54 +00:00
Jehan
6480ba2ad6 app, libgimp, pdb: new gimp_drawable_filter_set_aux_input() public API.
It is this way possible to set a drawable as auxiliary input to a filter
in C and GObject-Introspected bindings.

Note that such filters can only be merged, not appended
non-destructively for the time being.
2024-12-17 16:24:54 +00:00
Jehan
1fbd2acc84 app, pdb, plug-ins: get rid of (plug-in-rotate).
It can be reimplemented with (gimp-image-rotate) and
(gimp-item-transform-rotate-simple), with even more capabilities for the
latter.

The item rotate procedure is a bit more tricky though, since it takes
into account the selection. It means that either you want to just remove
the selection before (that's what I did here, because this script was
already losing the selection anyway), or you want to store the selection
(with (gimp-channel-copy)), then reapply it (very likely with
(gimp-channel-combine-masks)) at the end, after also rotating it
appropriately the same way as the image if needed.
2024-12-17 16:24:54 +00:00
Jehan
c26b2cd08f app, pdb, plug-ins: replace (plug-in-noisify).
For plug-in writers reference, these are equivalent:

- (plug-in-noisify RUN-NONINTERACTIVE theImage mLayer TRUE r g b a)
+ (gimp-drawable-merge-new-filter mLayer "gegl:noise-rgb" 0 LAYER-MODE-REPLACE 1.0
+                                 "independent" TRUE "red" r "green" g "blue" b "alpha" a
+                                 "correlated" FALSE "seed" (msrg-rand) "linear" TRUE)

Notes:

* When "independent" is FALSE, then you only need to set "red" (which is
  equivalent to a "value" field) and "alpha".
* Original plug-in was using the second value ('g', a.k.a. noise_2 in
  the PDB args) as alpha when the source drawable was grayscale. This
  logic is meaningless now. Just set "alpha".
* The PDB procedure was wrapping the operation between
  "gegl:cast-format" nodes to cast the input buffer to linear. This is
  useless as the "gegl:noise-rgb" has already a "linear" argument
  (defaulting to TRUE, but I specify it explicitly in this commit, for
  clarity) which requests linear input when set.
2024-12-17 16:24:54 +00:00
Jehan
dc4daa8396 app, pdb, plug-ins: replace (plug-in-emboss).
For plug-in writers reference, these are equivalent:

- (plug-in-emboss RUN-NONINTERACTIVE img mask-emboss 315.0 45.0 7 TRUE)
+ (gimp-drawable-merge-new-filter mask-emboss "gegl:emboss" 0 LAYER-MODE-REPLACE 1.0 "azimuth" 315.0 "elevation" 45.0 "depth" 7 "type" "emboss")

Note: the last arg, emboss, must be replaced by a string, "emboss" when
it was TRUE, "bumpmap" when FALSE.

Similarly to "gegl:gaussian-blur", the call to wrap_in_gamma_cast() was
useless because "gegl:emboss" already sets its output format to linear,
as well as its input format (through GeglOperationAreaFilter parent
class).
2024-12-17 16:24:54 +00:00
Jehan
1c4a00de78 app, pdb, plug-ins: replace (plug-in-gauss).
For plug-in writers reference, these are equivalent:

- (plug-in-gauss RUN-NONINTERACTIVE image layer hblur vblur 0)
+ (gimp-drawable-merge-new-filter layer "gegl:gaussian-blur" 0 LAYER-MODE-REPLACE 1.0 "std-dev-x" hblur "std-dev-y" vblur "filter" "auto")

Note: the method arg { AUTO (0), FIR (1), IIR (2) } is transformed into
a string in lowercase. E.g. 1 becomes "fir".

At first, I was a bit annoyed by the wrap_in_gamma_cast() call which
converts the buffer to linear first, but looking at "gegl:gaussian-blur"
implementation, it is just a meta op to 2 "gegl:gblur-1d" which anyway
sets input format (to various formats, but always linear).

This legacy cast code must be a remnant from older times where it might
have been needed. Clearly it's not anymore.
2024-12-17 16:24:54 +00:00
Jehan
b0ac59b421 app, pdb, plug-ins: replace (plug-in-plasma).
This time, it's a Python plug-in, which is a bit more involved than C or
Script-fu usage (where we have variable args one-liner function) though
it's not too bad either. It's actually very similar (on purpose) to how
PDB procedures are run.
2024-12-17 16:24:54 +00:00
Jehan
082ba3bb1f app, pdb, plug-ins: replace (plug-in-waves).
For plug-in writers reference, these are equivalent:

- (plug-in-waves RUN-NONINTERACTIVE
-                image
-                source-layer
-                amplitude
-                phase
-                wavelength
-                0
-                FALSE)
+
+ (let* (
+       (phi phase)
+       (width (car (gimp-drawable-get-width source-layer)))
+       (height (car (gimp-drawable-get-height source-layer)))
+       (aspect (/ width height))
+       )
+
+   (while (< phi 0)
+     (set! phi (+ phi 360.0))
+   )
+   (set! phi (/ (- (modulo phase 360.0) 180.0) 180.0))
+
+   (if (< aspect 0.1)
+     (set! aspect 0.1))
+   (if (> aspect 10.0)
+     (set! aspect 10.0))
+   (gimp-drawable-merge-new-filter source-layer "gegl:waves" 0 LAYER-MODE-REPLACE 1.0 "amplitude" amplitude "phi" phi
+                                                                                      "period" (* wavelength 2.0)
+                                                                                      "clamp" TRUE "aspect" aspect)
+ )

Notes:

* The old type argument is the negated value of "clamp".
* The last argument (reflective) was unused.
2024-12-17 16:24:54 +00:00
Jehan
c70ffcec91 app, pdb, plug-ins: replace (plug-in-threshold-alpha).
For plug-in writers reference, these are equivalent:

- (plug-in-threshold-alpha RUN-NONINTERACTIVE image layer threshold))
+ (gimp-drawable-merge-new-filter layer "gimp:threshold-alpha" 0 LAYER-MODE-REPLACE 1.0 "value" (/ threshold 255))

The main difference is that threshold arg was a [0; 255] int whereas
"value" is a [0.0; 1.0] double.

This commit also shows how to run filters in C plug-ins (file-ico here)
as a one-liner too, thanks to the varargs conviency function.
2024-12-17 16:24:54 +00:00
Jehan
3c0c4db614 app, pdb, plug-ins: replace (plug-in-spread).
For plug-in writers reference, these are equivalent:

- (plug-in-spread RUN-NONINTERACTIVE image layer inSpread inSpread)
+ (gimp-drawable-merge-new-filter layer "gegl:noise-spread" 0 LAYER-MODE-REPLACE 1.0 "amount-x" inSpread "amount-y" inSpread "seed" (msrg-rand))
2024-12-17 16:24:54 +00:00
Jehan
5af02d292a app, pdb, plug-ins: replace (plug-in-solid-noise).
For plug-in writers reference, these are equivalent:

- (plug-in-solid-noise RUN-NONINTERACTIVE image layer FALSE TRUE seed 2 2 2)
+ (let* ((width  (cadddr (gimp-drawable-mask-intersect layer)))
+        (height (caddr (cddr (gimp-drawable-mask-intersect layer)))))
+   (gimp-drawable-merge-new-filter layer "gegl:noise-solid" 0 LAYER-MODE-REPLACE 1.0 "tileable" FALSE "turbulent" TRUE "seed" seed
+                                                                                     "detail" 2 "x-size" 2.0 "y-size" 2.0
+                                                                                     "width" width "height" height)
+ )
2024-12-17 16:24:54 +00:00
Jehan
093b09ccc6 app, pdb, plug-ins: replace (plug-in-oilify).
For plug-in writers reference, these are equivalent:

- (plug-in-oilify RUN-NONINTERACTIVE image active-layer mask-size 0)
+ (gimp-drawable-merge-new-filter active-layer "gegl:oilify" 0 LAYER-MODE-REPLACE 1.0 "mask-radius" (max 1 (/ mask-size 2)) "use-inten" FALSE)
2024-12-17 16:24:54 +00:00
Jehan
dfeab826ce app, pdb, plug-ins: replace (plug-in-maze).
For plug-in writers reference:

- (plug-in-maze RUN-NONINTERACTIVE image active-layer 5 5 TRUE 0 seed 57 1)
+ (gimp-drawable-merge-new-filter active-layer "gegl:maze" 0 LAYER-MODE-REPLACE 1.0 "x" 5 "y" 5 "tileable" TRUE "algorithm-type" "depth-first"
+                                                                                   "seed" seed
+                                                                                   "fg-color" (car (gimp-context-get-foreground))
+                                                                                   "bg-color" (car (gimp-context-get-background)))

Notes:

* FG and BG colors were hardcoded to current context colors. It also
  means with the new API, you can use whatever else you want.
* The algorithm arg changes like this:
  - 0 -> "depth-first"
  - 0 -> "prim"
* multiple and offset args (the 2 last args) were bogus and were doing
  nothing already.
2024-12-17 16:24:54 +00:00
Jehan
7e81bdfa14 app, pdb, plug-ins: replace (plug-in-edge).
For plug-in writers reference:

- (plug-in-edge RUN-NONINTERACTIVE image layer 2 1 0)
+ (gimp-drawable-merge-new-filter layer "gegl:edge" 0 LAYER-MODE-REPLACE opacity "amount" 2.0 "border-behavior" "loop" "algorithm" "sobel")

The warpmode (before-last) arg has to be replaced like this (types from
GeglAbyssPolicy enum type):

* NONE (0)  -> "none"
* WRAP (1)  -> "loop"
* SMEAR (2) -> "clamp"
* BLACK (3) -> "black"

For edgemode (last arg), just use the name value in lowercase as string:
{ SOBEL (0), PREWITT (1), GRADIENT (2), ROBERTS (3), DIFFERENTIAL (4), LAPLACE (5) }
E.g. "differencial" instead of formerly 4.
2024-12-17 16:24:54 +00:00
Jehan
4cc50c6701 app, pdb, plug-ins: replace (plug-in-cubism).
For plug-in developers reference:
- (plug-in-cubism RUN-NONINTERACTIVE image layer tile_size saturation 0)
+ (gimp-drawable-merge-new-filter layer "gegl:cubism" 0 LAYER-MODE-REPLACE 1.0 "tile-size" tile_size "tile-saturation" saturation "bg-color" '(0 0 0))

Now you can even choose the bg-color (not just an int/enum of black and
background color only).
2024-12-17 16:24:54 +00:00
Jehan
f539f8fb62 app, libgimp, pdb, plug-ins: plug-in-c-astretch compat PDB proc removed.
For plug-in writers, here is how to replace it:

- (plug-in-c-astretch RUN-NONINTERACTIVE img drawable)
+ (gimp-drawable-merge-new-filter drawable "gegl:stretch-contrast" 0 LAYER-MODE-REPLACE 1.0 "keep-colors" FALSE)
2024-12-17 16:24:54 +00:00
Jehan
3da53852df app, libgimp, pdb: make sure default values of a filter's config are right.
When calling gimp_drawable_filter_get_config() the first time, we don't
want the config's properties to be at default values, but instead to be
set same as they are on core app.

On further calls though, we don't touch the values, because they may be
out-of-sync until the next call to gimp_drawable_filter_update().
2024-12-17 16:24:54 +00:00
Jehan
58b11d9d98 app, extensions, libgimp: new convenient function gimp_drawable_merge_new_filter().
Also adding an example of using it in the C goat exercise, though I
leave it commented out because showing code for direct GEGL usage is
also of interest.

As a side update, I am adding proper support of the NULL or empty string
value for the filter name. In this case, the default human-readable
title of the operation will be used instead.
2024-12-17 16:24:54 +00:00
Jehan
57a8449646 app, libgimp, pdb: add gimp_drawable_merge_filter(). 2024-12-17 16:24:54 +00:00
Jehan
a6187fc2a8 app: reorder the filter when "Merge filter" is checked at creation.
Without this, the filter order was right only when checking off then
then on the option, but the render was wrong when opening the filter
options dialog.
2024-12-17 16:24:54 +00:00
Jehan
b07a6986e0 app, libgimp, pdb: make GIMP_LAYER_MODE_REPLACE PDB-visible.
This is the actual default blend mode for filters so now that we have
API to set filters, this mode must be settable.
2024-12-17 16:24:54 +00:00
Jehan
c310573021 app, libgimp, pdb: new gimp_drawable_filter_(g|s)et_blend_mode() and gimp_drawable_filter_(g|s)et_opacity().
Similarly to the config arguments, the set functions are not directly
calling core. Instead they are queuing changes until
gimp_drawable_filter_update() is run.
2024-12-17 16:24:54 +00:00
Jehan
1587a8ec54 app, libgimp, pdb: new libgimp functions gimp_drawable_filter_new() and gimp_drawable_append_filter(). 2024-12-17 16:24:54 +00:00
Jehan
7715a875e0 app, libgimp, pdb: new gimp_drawable_filter_update() public API.
It is now possible to sync the GimpDrawableFilterConfig with core.

Another (simpler on usage) possibility could have been to sync
automatically when a property is updated. But considering that some
filters can be quite slow to render (especially in real-life usage when
working on possibly very big files), and especially that on bindings
with no variable args, scripts will likely have to edit properties one
by one, it could make editing multiple properties very slow. Therefore
the chosen solution is that editing properties stay local on libgimp and
all changed properties are synced with core at once (with a frozen
render until the end for single computation) when calling
gimp_drawable_filter_update().
2024-12-17 16:24:54 +00:00
Jehan
2cf6b637e7 app, libgimp, pdb: new GimpDrawableFilterConfig class.
This is mostly an empty shell whose goal is to serve as base class for
specific dynamically generated per-operation subclasses, which will have
properties mimicking the arguments of the GEGL operation.

Most of the fundamental type args will just use the base GLib param spec
types instead of GEGL ones.

As a special case, the GeglParamEnum arguments are transformed into
GimpChoice param specs on libgimp side. The reason is that most of the
time, enum types are created within the scope of an operation code only
and cannot be properly reconstructed over the wire. I could just
transform these into an int type (which is mostly what is done in GEGL
right now when running an op with such arg), but GimpChoice allow much
nicer string-type args, which make much more semantic code. This class
was initially created for plug-ins, but it would work very well to run
GEGL ops on drawables. So let's do it.

Finally add a gimp_drawable_filter_get_config() to request the current
config of a filter.

Note that right now, we can't do much with this config object other than
observing an operation args and default values. Being able to update a
filter's settings will come up in further commits.
2024-12-17 16:24:54 +00:00
Jehan
c49dec3416 app, libgimp, pdb: 4 new libgimp/PDB procedures for GimpDrawableFilter.
* gimp_drawable_filter_get_name
* gimp_drawable_filter_get_operation_name
* gimp_drawable_filter_get_visible
* gimp_drawable_filter_set_visible
2024-12-17 16:24:54 +00:00