tools, operation: Add support for loading PS Levels presets

Photoshop stores their Levels filter presets in an
.alv file. This patch adds support for loading these
and converting them to GIMP's Level format.

The .alv file is binary, and consists of a 2 byte version
followed by 29 groups of 2 byte values for input,
output, and gamma. We only read the first four for
now, since GIMP only supports composite and then
R, G, and B individual channels.
This commit is contained in:
Alx Sa 2025-04-12 02:23:36 +00:00
parent f3afd91a95
commit 3879520cc2
3 changed files with 108 additions and 1 deletions

View file

@ -992,3 +992,98 @@ gimp_levels_config_save_cruft (GimpLevelsConfig *config,
return TRUE; return TRUE;
} }
gboolean
gimp_levels_config_load_alv (GimpLevelsConfig *config,
GInputStream *input,
GError **error)
{
GDataInputStream *data_input;
guint16 version = 0;
gint low_input[4];
gint high_input[4];
gint low_output[4];
gint high_output[4];
gdouble gamma[4];
g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), FALSE);
g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
data_input = g_data_input_stream_new (input);
g_data_input_stream_set_byte_order (data_input,
G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
version = g_data_input_stream_read_uint16 (data_input, NULL, error);
if (! version)
goto error;
/* TODO: Photoshop Level presets store 29 levels, for channels beyond
* RGB. When we can support those, we should update the read limit */
for (gint i = 0; i < 4; i++)
{
low_input[i] = g_data_input_stream_read_uint16 (data_input, NULL, error);
if (error && *error)
goto error;
high_input[i] = g_data_input_stream_read_uint16 (data_input, NULL,
error);
if (error && *error)
goto error;
low_output[i] = g_data_input_stream_read_uint16 (data_input, NULL,
error);
if (error && *error)
goto error;
high_output[i] = g_data_input_stream_read_uint16 (data_input, NULL,
error);
if (error && *error)
goto error;
gamma[i] = g_data_input_stream_read_int16 (data_input, NULL, error);
if (error && *error)
goto error;
}
g_object_unref (data_input);
g_object_freeze_notify (G_OBJECT (config));
for (gint i = 0; i < 4; i++)
{
config->low_input[i] = low_input[i] / 253.0;
config->high_input[i] = high_input[i] / 255.0;
config->gamma[i] = gamma[i] / 100.0;
config->low_output[i] = low_output[i] / 255.0;
config->high_output[i] = high_output[i] / 255.0;
}
config->trc = GIMP_TRC_NON_LINEAR;
config->clamp_input = TRUE;
config->clamp_output = TRUE;
g_object_notify (G_OBJECT (config), "trc");
g_object_notify (G_OBJECT (config), "low-input");
g_object_notify (G_OBJECT (config), "high-input");
g_object_notify (G_OBJECT (config), "clamp-input");
g_object_notify (G_OBJECT (config), "gamma");
g_object_notify (G_OBJECT (config), "low-output");
g_object_notify (G_OBJECT (config), "high-output");
g_object_notify (G_OBJECT (config), "clamp-output");
g_object_thaw_notify (G_OBJECT (config));
return TRUE;
error:
g_object_unref (data_input);
if (error && *error)
g_prefix_error (error, _("parse error"));
else
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
_("parse error"));
return FALSE;
}

View file

@ -89,5 +89,8 @@ gboolean gimp_levels_config_save_cruft (GimpLevelsConfig *config,
GOutputStream *output, GOutputStream *output,
GError **error); GError **error);
gboolean gimp_levels_config_load_alv (GimpLevelsConfig *config,
GInputStream *input,
GError **error);
#endif /* __GIMP_LEVELS_CONFIG_H__ */ #endif /* __GIMP_LEVELS_CONFIG_H__ */

View file

@ -35,6 +35,7 @@
#include "core/gimp-gui.h" #include "core/gimp-gui.h"
#include "core/gimpasync.h" #include "core/gimpasync.h"
#include "core/gimpcancelable.h" #include "core/gimpcancelable.h"
#include "core/gimpdata.h"
#include "core/gimpdrawable.h" #include "core/gimpdrawable.h"
#include "core/gimpdrawable-histogram.h" #include "core/gimpdrawable-histogram.h"
#include "core/gimperror.h" #include "core/gimperror.h"
@ -742,7 +743,11 @@ gimp_levels_tool_settings_import (GimpFilterTool *filter_tool,
&bytes_read, NULL, error) || &bytes_read, NULL, error) ||
bytes_read != sizeof (header)) bytes_read != sizeof (header))
{ {
g_prefix_error (error, _("Could not read header: ")); if (error && *error)
g_prefix_error (error, _("Could not read header: "));
else
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
_("Could not read header: "));
return FALSE; return FALSE;
} }
@ -751,6 +756,10 @@ gimp_levels_tool_settings_import (GimpFilterTool *filter_tool,
if (g_str_has_prefix (header, "# GIMP Levels File\n")) if (g_str_has_prefix (header, "# GIMP Levels File\n"))
return gimp_levels_config_load_cruft (config, input, error); return gimp_levels_config_load_cruft (config, input, error);
/* Check if we're loading a Photoshop Levels preset .alv file */
if (bytes_read > 2 && header[0] == 0 && (header[1] == 2))
return gimp_levels_config_load_alv (config, input, error);
return GIMP_FILTER_TOOL_CLASS (parent_class)->settings_import (filter_tool, return GIMP_FILTER_TOOL_CLASS (parent_class)->settings_import (filter_tool,
input, input,
error); error);