mirror of https://github.com/GNOME/gimp.git
plug-ins: port file-webp to GimpPlugIn and libgimp objects
This commit is contained in:
parent
9f3bc2bd7a
commit
3636541b42
|
@ -22,7 +22,6 @@ AM_LDFLAGS = $(mwindows)
|
|||
libexecdir = $(gimpplugindir)/plug-ins/file-webp
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-DGIMP_DEPRECATED_REPLACE_NEW_API \
|
||||
-I$(top_srcdir) \
|
||||
$(GTK_CFLAGS) \
|
||||
$(EXIF_CFLAGS) \
|
||||
|
|
|
@ -81,7 +81,7 @@ show_maxkeyframe_hints (GtkAdjustment *adj,
|
|||
|
||||
gboolean
|
||||
save_dialog (WebPSaveParams *params,
|
||||
gint32 image_ID)
|
||||
GimpImage *image)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *vbox;
|
||||
|
@ -95,13 +95,17 @@ save_dialog (WebPSaveParams *params,
|
|||
GtkWidget *combo;
|
||||
GtkAdjustment *quality_scale;
|
||||
GtkAdjustment *alpha_quality_scale;
|
||||
GList *list;
|
||||
gint32 nlayers;
|
||||
gboolean animation_supported = FALSE;
|
||||
gboolean run;
|
||||
gchar *text;
|
||||
gint row = 0;
|
||||
|
||||
g_free (gimp_image_get_layers (image_ID, &nlayers));
|
||||
list = gimp_image_get_layers (image);
|
||||
nlayers = g_list_length (list);
|
||||
g_list_free (list);
|
||||
|
||||
animation_supported = nlayers > 1;
|
||||
|
||||
/* Create the dialog */
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
|
||||
gboolean save_dialog (WebPSaveParams *params,
|
||||
gint32 image_ID);
|
||||
GimpImage *image);
|
||||
|
||||
|
||||
#endif /* __WEBP_DIALOG_H__ */
|
||||
|
|
|
@ -40,27 +40,27 @@
|
|||
|
||||
|
||||
static void
|
||||
create_layer (gint32 image_ID,
|
||||
uint8_t *layer_data,
|
||||
gint32 position,
|
||||
gchar *name,
|
||||
gint width,
|
||||
gint height)
|
||||
create_layer (GimpImage *image,
|
||||
uint8_t *layer_data,
|
||||
gint32 position,
|
||||
gchar *name,
|
||||
gint width,
|
||||
gint height)
|
||||
{
|
||||
gint32 layer_ID;
|
||||
GimpLayer *layer;
|
||||
GeglBuffer *buffer;
|
||||
GeglRectangle extent;
|
||||
|
||||
layer_ID = gimp_layer_new (image_ID, name,
|
||||
width, height,
|
||||
GIMP_RGBA_IMAGE,
|
||||
100,
|
||||
gimp_image_get_default_new_layer_mode (image_ID));
|
||||
layer = gimp_layer_new (image, name,
|
||||
width, height,
|
||||
GIMP_RGBA_IMAGE,
|
||||
100,
|
||||
gimp_image_get_default_new_layer_mode (image));
|
||||
|
||||
gimp_image_insert_layer (image_ID, layer_ID, -1, position);
|
||||
gimp_image_insert_layer (image, layer, NULL, position);
|
||||
|
||||
/* Retrieve the buffer for the layer */
|
||||
buffer = gimp_drawable_get_buffer (layer_ID);
|
||||
buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
|
||||
|
||||
/* Copy the image data to the region */
|
||||
gegl_rectangle_set (&extent, 0, 0, width, height);
|
||||
|
@ -72,7 +72,7 @@ create_layer (gint32 image_ID,
|
|||
g_object_unref (buffer);
|
||||
}
|
||||
|
||||
gint32
|
||||
GimpImage *
|
||||
load_image (const gchar *filename,
|
||||
gboolean interactive,
|
||||
GError **error)
|
||||
|
@ -81,7 +81,7 @@ load_image (const gchar *filename,
|
|||
gsize indatalen;
|
||||
gint width;
|
||||
gint height;
|
||||
gint32 image_ID;
|
||||
GimpImage *image;
|
||||
WebPMux *mux;
|
||||
WebPData wp_data;
|
||||
GimpColorProfile *profile = NULL;
|
||||
|
@ -97,7 +97,7 @@ load_image (const gchar *filename,
|
|||
&indatalen,
|
||||
error))
|
||||
{
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Validate WebP data */
|
||||
|
@ -106,7 +106,7 @@ load_image (const gchar *filename,
|
|||
g_set_error (error, G_FILE_ERROR, 0,
|
||||
_("Invalid WebP file '%s'"),
|
||||
gimp_filename_to_utf8 (filename));
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wp_data.bytes = indata;
|
||||
|
@ -114,7 +114,7 @@ load_image (const gchar *filename,
|
|||
|
||||
mux = WebPMuxCreate (&wp_data, 1);
|
||||
if (! mux)
|
||||
return -1;
|
||||
return NULL;
|
||||
|
||||
WebPMuxGetFeatures (mux, &flags);
|
||||
|
||||
|
@ -134,7 +134,7 @@ load_image (const gchar *filename,
|
|||
/* TODO: check if an alpha channel is present */
|
||||
|
||||
/* Create the new image and associated layer */
|
||||
image_ID = gimp_image_new (width, height, GIMP_RGB);
|
||||
image = gimp_image_new (width, height, GIMP_RGB);
|
||||
|
||||
if (icc)
|
||||
{
|
||||
|
@ -144,7 +144,7 @@ load_image (const gchar *filename,
|
|||
profile = gimp_color_profile_new_from_icc_profile (icc_profile.bytes,
|
||||
icc_profile.size, NULL);
|
||||
if (profile)
|
||||
gimp_image_set_color_profile (image_ID, profile);
|
||||
gimp_image_set_color_profile (image, profile);
|
||||
}
|
||||
|
||||
if (! animation)
|
||||
|
@ -158,10 +158,10 @@ load_image (const gchar *filename,
|
|||
if (! outdata)
|
||||
{
|
||||
WebPMuxDelete (mux);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
create_layer (image_ID, outdata, 0, _("Background"),
|
||||
create_layer (image, outdata, 0, _("Background"),
|
||||
width, height);
|
||||
|
||||
/* Free the image data */
|
||||
|
@ -189,7 +189,7 @@ load_image (const gchar *filename,
|
|||
}
|
||||
|
||||
WebPMuxDelete (mux);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* dec_options.color_mode is MODE_RGBA by default here */
|
||||
|
@ -230,7 +230,7 @@ load_image (const gchar *filename,
|
|||
}
|
||||
|
||||
name = g_strdup_printf (_("Frame %d (%dms)"), frame_num, iter.duration);
|
||||
create_layer (image_ID, outdata, 0, name, width, height);
|
||||
create_layer (image, outdata, 0, name, width, height);
|
||||
g_free (name);
|
||||
|
||||
frame_num++;
|
||||
|
@ -265,7 +265,7 @@ load_image (const gchar *filename,
|
|||
}
|
||||
|
||||
file = g_file_new_for_path (filename);
|
||||
metadata = gimp_image_metadata_load_prepare (image_ID, "image/webp",
|
||||
metadata = gimp_image_metadata_load_prepare (image, "image/webp",
|
||||
file, NULL);
|
||||
if (metadata)
|
||||
{
|
||||
|
@ -274,7 +274,7 @@ load_image (const gchar *filename,
|
|||
if (profile)
|
||||
flags &= ~GIMP_METADATA_LOAD_COLORSPACE;
|
||||
|
||||
gimp_image_metadata_load_finish (image_ID, "image/webp",
|
||||
gimp_image_metadata_load_finish (image, "image/webp",
|
||||
metadata, flags,
|
||||
interactive);
|
||||
g_object_unref (metadata);
|
||||
|
@ -285,10 +285,10 @@ load_image (const gchar *filename,
|
|||
|
||||
WebPMuxDelete (mux);
|
||||
|
||||
gimp_image_set_filename (image_ID, filename);
|
||||
gimp_image_set_filename (image, filename);
|
||||
|
||||
if (profile)
|
||||
g_object_unref (profile);
|
||||
|
||||
return image_ID;
|
||||
return image;
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
#define __WEBP_LOAD_H__
|
||||
|
||||
|
||||
gint32 load_image (const gchar *filename,
|
||||
gboolean interactive,
|
||||
GError **error);
|
||||
GimpImage * load_image (const gchar *filename,
|
||||
gboolean interactive,
|
||||
GError **error);
|
||||
|
||||
|
||||
#endif /* __WEBP_LOAD_H__ */
|
||||
|
|
|
@ -54,20 +54,20 @@ const gchar * webp_error_string (WebPEncodingError error_code);
|
|||
|
||||
gboolean save_layer (const gchar *filename,
|
||||
gint32 nLayers,
|
||||
gint32 image_ID,
|
||||
gint32 drawable_ID,
|
||||
GimpImage *image,
|
||||
GimpDrawable *drawable,
|
||||
WebPSaveParams *params,
|
||||
GError **error);
|
||||
|
||||
gboolean save_animation (const gchar *filename,
|
||||
gint32 nLayers,
|
||||
gint32 *allLayers,
|
||||
gint32 image_ID,
|
||||
gint32 drawable_ID,
|
||||
GList *layers,
|
||||
GimpImage *image,
|
||||
GimpDrawable *drawable,
|
||||
WebPSaveParams *params,
|
||||
GError **error);
|
||||
|
||||
static void webp_decide_output (gint32 image_ID,
|
||||
static void webp_decide_output (GimpImage *image,
|
||||
WebPSaveParams *params,
|
||||
GimpColorProfile **profile,
|
||||
gboolean *out_linear);
|
||||
|
@ -142,8 +142,8 @@ webp_error_string (WebPEncodingError error_code)
|
|||
gboolean
|
||||
save_layer (const gchar *filename,
|
||||
gint32 nLayers,
|
||||
gint32 image_ID,
|
||||
gint32 drawable_ID,
|
||||
GimpImage *image,
|
||||
GimpDrawable *drawable,
|
||||
WebPSaveParams *params,
|
||||
GError **error)
|
||||
{
|
||||
|
@ -169,7 +169,7 @@ save_layer (const gchar *filename,
|
|||
gboolean out_linear = FALSE;
|
||||
int res;
|
||||
|
||||
webp_decide_output (image_ID, params, &profile, &out_linear);
|
||||
webp_decide_output (image, params, &profile, &out_linear);
|
||||
if (profile)
|
||||
{
|
||||
space = gimp_color_profile_get_space (profile,
|
||||
|
@ -186,7 +186,7 @@ save_layer (const gchar *filename,
|
|||
|
||||
}
|
||||
if (! space)
|
||||
space = gimp_drawable_get_format (drawable_ID);
|
||||
space = gimp_drawable_get_format (drawable);
|
||||
|
||||
/* The do...while() loop is a neat little trick that makes it easier
|
||||
* to jump to error handling code while still ensuring proper
|
||||
|
@ -211,7 +211,7 @@ save_layer (const gchar *filename,
|
|||
}
|
||||
|
||||
/* Obtain the drawable type */
|
||||
has_alpha = gimp_drawable_has_alpha (drawable_ID);
|
||||
has_alpha = gimp_drawable_has_alpha (drawable);
|
||||
|
||||
if (has_alpha)
|
||||
{
|
||||
|
@ -232,7 +232,7 @@ save_layer (const gchar *filename,
|
|||
bpp = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
/* Retrieve the buffer for the layer */
|
||||
geglbuffer = gimp_drawable_get_buffer (drawable_ID);
|
||||
geglbuffer = gimp_drawable_get_buffer (drawable);
|
||||
extent = *gegl_buffer_get_extent (geglbuffer);
|
||||
w = extent.width;
|
||||
h = extent.height;
|
||||
|
@ -413,12 +413,12 @@ parse_ms_tag (const gchar *str)
|
|||
}
|
||||
|
||||
static gint
|
||||
get_layer_delay (gint32 layer)
|
||||
get_layer_delay (GimpLayer *layer)
|
||||
{
|
||||
gchar *layer_name;
|
||||
gint delay_ms;
|
||||
|
||||
layer_name = gimp_item_get_name (layer);
|
||||
layer_name = gimp_item_get_name (GIMP_ITEM (layer));
|
||||
delay_ms = parse_ms_tag (layer_name);
|
||||
g_free (layer_name);
|
||||
|
||||
|
@ -446,12 +446,12 @@ parse_combine (const char* str)
|
|||
}
|
||||
|
||||
static gint
|
||||
get_layer_needs_combine (gint32 layer)
|
||||
get_layer_needs_combine (GimpLayer *layer)
|
||||
{
|
||||
gchar *layer_name;
|
||||
gboolean needs_combine;
|
||||
|
||||
layer_name = gimp_item_get_name (layer);
|
||||
layer_name = gimp_item_get_name (GIMP_ITEM (layer));
|
||||
needs_combine = parse_combine (layer_name);
|
||||
g_free (layer_name);
|
||||
|
||||
|
@ -501,9 +501,9 @@ combine_buffers (GeglBuffer *layer_buffer,
|
|||
gboolean
|
||||
save_animation (const gchar *filename,
|
||||
gint32 nLayers,
|
||||
gint32 *allLayers,
|
||||
gint32 image_ID,
|
||||
gint32 drawable_ID,
|
||||
GList *layers,
|
||||
GimpImage *image,
|
||||
GimpDrawable *drawable,
|
||||
WebPSaveParams *params,
|
||||
GError **error)
|
||||
{
|
||||
|
@ -528,7 +528,7 @@ save_animation (const gchar *filename,
|
|||
if (nLayers < 1)
|
||||
return FALSE;
|
||||
|
||||
webp_decide_output (image_ID, params, &profile, &out_linear);
|
||||
webp_decide_output (image, params, &profile, &out_linear);
|
||||
if (profile)
|
||||
{
|
||||
space = gimp_color_profile_get_space (profile,
|
||||
|
@ -545,14 +545,15 @@ save_animation (const gchar *filename,
|
|||
|
||||
}
|
||||
if (! space)
|
||||
space = gimp_drawable_get_format (drawable_ID);
|
||||
space = gimp_drawable_get_format (drawable);
|
||||
|
||||
gimp_image_undo_freeze (image_ID);
|
||||
gimp_image_undo_freeze (image);
|
||||
|
||||
WebPDataInit (&webp_data);
|
||||
|
||||
do
|
||||
{
|
||||
GList *list;
|
||||
gint loop;
|
||||
gint default_delay = params->delay;
|
||||
gboolean force_delay = params->force_delay;
|
||||
|
@ -593,17 +594,22 @@ save_animation (const gchar *filename,
|
|||
enc_options.kmin = params->kf_distance - 1;
|
||||
}
|
||||
|
||||
for (loop = 0; loop < nLayers; loop++)
|
||||
for (list = g_list_last (layers), loop = 0;
|
||||
list && loop < nLayers;
|
||||
list = g_list_previous (list), loop++)
|
||||
{
|
||||
GeglBuffer *geglbuffer;
|
||||
GeglBuffer *current_frame;
|
||||
GeglRectangle extent;
|
||||
WebPConfig config;
|
||||
WebPPicture picture;
|
||||
WebPMemoryWriter mw = { 0 };
|
||||
gint32 drawable = allLayers[nLayers - 1 - loop];
|
||||
gint delay = get_layer_delay (drawable);
|
||||
gboolean needs_combine = get_layer_needs_combine (drawable);
|
||||
WebPMemoryWriter mw = { 0 };
|
||||
GimpDrawable *drawable = list->data;
|
||||
gint delay;
|
||||
gboolean needs_combine;
|
||||
|
||||
delay = get_layer_delay (GIMP_LAYER (drawable));
|
||||
needs_combine = get_layer_needs_combine (GIMP_LAYER (drawable));
|
||||
|
||||
/* Obtain the drawable type */
|
||||
has_alpha = gimp_drawable_has_alpha (drawable);
|
||||
|
@ -627,7 +633,7 @@ save_animation (const gchar *filename,
|
|||
bpp = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
/* fix layers to avoid offset errors */
|
||||
gimp_layer_resize_to_image_size (drawable);
|
||||
gimp_layer_resize_to_image_size (GIMP_LAYER (drawable));
|
||||
|
||||
/* Retrieve the buffer for the layer */
|
||||
geglbuffer = gimp_drawable_get_buffer (drawable);
|
||||
|
@ -802,8 +808,8 @@ save_animation (const gchar *filename,
|
|||
|
||||
gboolean
|
||||
save_image (const gchar *filename,
|
||||
gint32 image_ID,
|
||||
gint32 drawable_ID,
|
||||
GimpImage *image,
|
||||
GimpDrawable *drawable,
|
||||
GimpMetadata *metadata,
|
||||
GimpMetadataSaveFlags metadata_flags,
|
||||
WebPSaveParams *params,
|
||||
|
@ -811,29 +817,27 @@ save_image (const gchar *filename,
|
|||
{
|
||||
GFile *file;
|
||||
gboolean status = FALSE;
|
||||
gint32 *layers;
|
||||
gint nlayers;
|
||||
GList *layers;
|
||||
|
||||
layers = gimp_image_get_layers (image_ID, &nlayers);
|
||||
layers = gimp_image_get_layers (image);
|
||||
|
||||
if (nlayers == 0)
|
||||
{
|
||||
g_free (layers);
|
||||
return FALSE;
|
||||
}
|
||||
if (! layers)
|
||||
return FALSE;
|
||||
|
||||
g_printerr ("Saving WebP file %s\n", filename);
|
||||
|
||||
if (params->animation)
|
||||
{
|
||||
status = save_animation (filename,
|
||||
nlayers, layers, image_ID, drawable_ID, params,
|
||||
g_list_length (layers), layers,
|
||||
image, drawable, params,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = save_layer (filename,
|
||||
nlayers, image_ID, drawable_ID, params, error);
|
||||
g_list_length (layers),
|
||||
image, drawable, params, error);
|
||||
}
|
||||
|
||||
g_free (layers);
|
||||
|
@ -866,7 +870,7 @@ save_image (const gchar *filename,
|
|||
metadata_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
|
||||
|
||||
file = g_file_new_for_path (filename);
|
||||
gimp_image_metadata_save_finish (image_ID,
|
||||
gimp_image_metadata_save_finish (image,
|
||||
"image/webp",
|
||||
metadata, metadata_flags,
|
||||
file, NULL);
|
||||
|
@ -878,7 +882,7 @@ save_image (const gchar *filename,
|
|||
}
|
||||
|
||||
static void
|
||||
webp_decide_output (gint32 image_ID,
|
||||
webp_decide_output (GimpImage *image,
|
||||
WebPSaveParams *params,
|
||||
GimpColorProfile **profile,
|
||||
gboolean *out_linear)
|
||||
|
@ -888,7 +892,7 @@ webp_decide_output (gint32 image_ID,
|
|||
*out_linear = FALSE;
|
||||
if (params->profile)
|
||||
{
|
||||
*profile = gimp_image_get_color_profile (image_ID);
|
||||
*profile = gimp_image_get_color_profile (image);
|
||||
|
||||
/* If a profile is explicitly set, follow its TRC, whatever the
|
||||
* storage format.
|
||||
|
@ -905,11 +909,11 @@ webp_decide_output (gint32 image_ID,
|
|||
if (! *profile)
|
||||
{
|
||||
/* There is always an effective profile. */
|
||||
*profile = gimp_image_get_effective_color_profile (image_ID);
|
||||
*profile = gimp_image_get_effective_color_profile (image);
|
||||
|
||||
if (gimp_color_profile_is_linear (*profile))
|
||||
{
|
||||
if (gimp_image_get_precision (image_ID) != GIMP_PRECISION_U8_LINEAR)
|
||||
if (gimp_image_get_precision (image) != GIMP_PRECISION_U8_LINEAR)
|
||||
{
|
||||
/* If stored data was linear, let's convert the profile. */
|
||||
GimpColorProfile *saved_profile;
|
||||
|
|
|
@ -43,8 +43,8 @@ typedef struct
|
|||
|
||||
|
||||
gboolean save_image (const gchar *filename,
|
||||
gint32 image_ID,
|
||||
gint32 drawable_ID,
|
||||
GimpImage *image,
|
||||
GimpDrawable *drawable,
|
||||
GimpMetadata *metadata,
|
||||
GimpMetadataSaveFlags metadata_flags,
|
||||
WebPSaveParams *params,
|
||||
|
|
|
@ -36,288 +36,365 @@
|
|||
#include "libgimp/stdplugins-intl.h"
|
||||
|
||||
|
||||
static void query (void);
|
||||
static void run (const gchar *name,
|
||||
gint nparams,
|
||||
const GimpParam *param,
|
||||
gint *nreturn_vals,
|
||||
GimpParam **return_vals);
|
||||
typedef struct _Webp Webp;
|
||||
typedef struct _WebpClass WebpClass;
|
||||
|
||||
|
||||
const GimpPlugInInfo PLUG_IN_INFO =
|
||||
struct _Webp
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
query,
|
||||
run
|
||||
GimpPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _WebpClass
|
||||
{
|
||||
GimpPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
MAIN()
|
||||
#define WEBP_TYPE (webp_get_type ())
|
||||
#define WEBP (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBP_TYPE, Webp))
|
||||
|
||||
GType webp_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * webp_query_procedures (GimpPlugIn *plug_in);
|
||||
static GimpProcedure * webp_create_procedure (GimpPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static GimpValueArray * webp_load (GimpProcedure *procedure,
|
||||
GimpRunMode run_mode,
|
||||
GFile *file,
|
||||
const GimpValueArray *args,
|
||||
gpointer run_data);
|
||||
static GimpValueArray * webp_save (GimpProcedure *procedure,
|
||||
GimpRunMode run_mode,
|
||||
GimpImage *image,
|
||||
GimpDrawable *drawable,
|
||||
GFile *file,
|
||||
const GimpValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Webp, webp, GIMP_TYPE_PLUG_IN)
|
||||
|
||||
GIMP_MAIN (WEBP_TYPE)
|
||||
|
||||
|
||||
static void
|
||||
query (void)
|
||||
webp_class_init (WebpClass *klass)
|
||||
{
|
||||
static const GimpParamDef load_arguments[] =
|
||||
{
|
||||
{ GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
|
||||
{ GIMP_PDB_STRING, "filename", "The name of the file to load" },
|
||||
{ GIMP_PDB_STRING, "raw-filename", "The name entered" }
|
||||
};
|
||||
GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);
|
||||
|
||||
static const GimpParamDef load_return_values[] =
|
||||
{
|
||||
{ GIMP_PDB_IMAGE, "image", "Output image" }
|
||||
};
|
||||
|
||||
static const GimpParamDef save_arguments[] =
|
||||
{
|
||||
{ GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
|
||||
{ GIMP_PDB_IMAGE, "image", "Input image" },
|
||||
{ GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
|
||||
{ GIMP_PDB_STRING, "filename", "The name of the file to save the image to" },
|
||||
{ GIMP_PDB_STRING, "raw-filename", "The name entered" },
|
||||
{ GIMP_PDB_INT32, "preset", "preset (Default=0, Picture=1, Photo=2, Drawing=3, Icon=4, Text=5)" },
|
||||
{ GIMP_PDB_INT32, "lossless", "Use lossless encoding (0/1)" },
|
||||
{ GIMP_PDB_FLOAT, "quality", "Quality of the image (0 <= quality <= 100)" },
|
||||
{ GIMP_PDB_FLOAT, "alpha-quality", "Quality of the image's alpha channel (0 <= alpha-quality <= 100)" },
|
||||
{ GIMP_PDB_INT32, "animation", "Use layers for animation (0/1)" },
|
||||
{ GIMP_PDB_INT32, "anim-loop", "Loop animation infinitely (0/1)" },
|
||||
{ GIMP_PDB_INT32, "minimize-size", "Minimize animation size (0/1)" },
|
||||
{ GIMP_PDB_INT32, "kf-distance", "Maximum distance between key-frames (>=0)" },
|
||||
{ GIMP_PDB_INT32, "exif", "Toggle saving exif data (0/1)" },
|
||||
{ GIMP_PDB_INT32, "iptc", "Toggle saving iptc data (0/1)" },
|
||||
{ GIMP_PDB_INT32, "xmp", "Toggle saving xmp data (0/1)" },
|
||||
{ GIMP_PDB_INT32, "delay", "Delay to use when timestamps are not available or forced" },
|
||||
{ GIMP_PDB_INT32, "force-delay", "Force delay on all frames" }
|
||||
};
|
||||
|
||||
gimp_install_procedure (LOAD_PROC,
|
||||
"Loads images in the WebP file format",
|
||||
"Loads images in the WebP file format",
|
||||
"Nathan Osman, Ben Touchette",
|
||||
"(C) 2015-2016 Nathan Osman, (C) 2016 Ben Touchette",
|
||||
"2015,2016",
|
||||
N_("WebP image"),
|
||||
NULL,
|
||||
GIMP_PLUGIN,
|
||||
G_N_ELEMENTS (load_arguments),
|
||||
G_N_ELEMENTS (load_return_values),
|
||||
load_arguments,
|
||||
load_return_values);
|
||||
|
||||
gimp_register_file_handler_mime (LOAD_PROC, "image/webp");
|
||||
gimp_register_load_handler (LOAD_PROC, "webp", "");
|
||||
gimp_register_magic_load_handler (LOAD_PROC,
|
||||
"webp",
|
||||
"",
|
||||
"8,string,WEBP");
|
||||
|
||||
gimp_install_procedure (SAVE_PROC,
|
||||
"Saves files in the WebP image format",
|
||||
"Saves files in the WebP image format",
|
||||
"Nathan Osman, Ben Touchette",
|
||||
"(C) 2015-2016 Nathan Osman, (C) 2016 Ben Touchette",
|
||||
"2015,2016",
|
||||
N_("WebP image"),
|
||||
"RGB*, GRAY*, INDEXED*",
|
||||
GIMP_PLUGIN,
|
||||
G_N_ELEMENTS (save_arguments),
|
||||
0,
|
||||
save_arguments,
|
||||
NULL);
|
||||
|
||||
gimp_register_file_handler_mime (SAVE_PROC, "image/webp");
|
||||
gimp_register_save_handler (SAVE_PROC, "webp", "");
|
||||
plug_in_class->query_procedures = webp_query_procedures;
|
||||
plug_in_class->create_procedure = webp_create_procedure;
|
||||
}
|
||||
|
||||
static void
|
||||
run (const gchar *name,
|
||||
gint nparams,
|
||||
const GimpParam *param,
|
||||
gint *nreturn_vals,
|
||||
GimpParam **return_vals)
|
||||
webp_init (Webp *webp)
|
||||
{
|
||||
static GimpParam values[2];
|
||||
GimpRunMode run_mode;
|
||||
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
||||
gint32 image_ID;
|
||||
gint32 drawable_ID;
|
||||
GError *error = NULL;
|
||||
}
|
||||
|
||||
static GList *
|
||||
webp_query_procedures (GimpPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC));
|
||||
list = g_list_append (list, g_strdup (SAVE_PROC));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static GimpProcedure *
|
||||
webp_create_procedure (GimpPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
GimpProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = gimp_load_procedure_new (plug_in, name, GIMP_PLUGIN,
|
||||
webp_load, NULL, NULL);
|
||||
|
||||
gimp_procedure_set_menu_label (procedure, N_("WebP image"));
|
||||
|
||||
gimp_procedure_set_documentation (procedure,
|
||||
"Loads images in the WebP file format",
|
||||
"Loads images in the WebP file format",
|
||||
name);
|
||||
gimp_procedure_set_attribution (procedure,
|
||||
"Nathan Osman, Ben Touchette",
|
||||
"(C) 2015-2016 Nathan Osman, "
|
||||
"(C) 2016 Ben Touchette",
|
||||
"2015,2016");
|
||||
|
||||
gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
|
||||
"image/webp");
|
||||
gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
|
||||
"webp");
|
||||
gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
|
||||
"8,string,WEBP");
|
||||
}
|
||||
else if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = gimp_save_procedure_new (plug_in, name, GIMP_PLUGIN,
|
||||
webp_save, NULL, NULL);
|
||||
|
||||
gimp_procedure_set_image_types (procedure, "*");
|
||||
|
||||
gimp_procedure_set_menu_label (procedure, N_("WebP image"));
|
||||
|
||||
gimp_procedure_set_documentation (procedure,
|
||||
"Saves files in the WebP image format",
|
||||
"Saves files in the WebP image format",
|
||||
name);
|
||||
gimp_procedure_set_attribution (procedure,
|
||||
"Nathan Osman, Ben Touchette",
|
||||
"(C) 2015-2016 Nathan Osman, "
|
||||
"(C) 2016 Ben Touchette",
|
||||
"2015,2016");
|
||||
|
||||
gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
|
||||
"image/webp");
|
||||
gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
|
||||
"webp");
|
||||
|
||||
GIMP_PROC_ARG_INT (procedure, "preset",
|
||||
"Preset",
|
||||
"Preset (Default=0, Picture=1, Photo=2, Drawing=3, "
|
||||
"Icon=4, Text=5)",
|
||||
0, 5, WEBP_PRESET_DEFAULT,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_BOOLEAN (procedure, "lossless",
|
||||
"Lossless",
|
||||
"Use lossless encoding",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_DOUBLE (procedure, "quality",
|
||||
"Quality",
|
||||
"Quality of the image",
|
||||
0, 100, 90,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_DOUBLE (procedure, "alpha-quality",
|
||||
"Alpha quality",
|
||||
"Quality of the image's alpha channel",
|
||||
0, 100, 100,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_BOOLEAN (procedure, "animation",
|
||||
"Animation",
|
||||
"Use layers for animation",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_BOOLEAN (procedure, "animation-loop",
|
||||
"Animation loop",
|
||||
"Loop animation infinitely",
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_BOOLEAN (procedure, "minimize-size",
|
||||
"Minimize size",
|
||||
"Minimize animation size",
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_INT (procedure, "kf-distance",
|
||||
"KF distance",
|
||||
"Maximum distance between key-frames",
|
||||
0, G_MAXINT, 50,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_BOOLEAN (procedure, "exif",
|
||||
"EXIF",
|
||||
"Toggle saving exif data",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_BOOLEAN (procedure, "iptc",
|
||||
"IPTC",
|
||||
"Toggle saving iptc data",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_BOOLEAN (procedure, "xmp",
|
||||
"XMP",
|
||||
"Toggle saving xmp data",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_INT (procedure, "delay",
|
||||
"Delay",
|
||||
"Delay to use when timestamps are not available "
|
||||
"or forced",
|
||||
0, G_MAXINT, 200,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
GIMP_PROC_ARG_BOOLEAN (procedure, "force-delay",
|
||||
"Force delay",
|
||||
"Force delay on all frames",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static GimpValueArray *
|
||||
webp_load (GimpProcedure *procedure,
|
||||
GimpRunMode run_mode,
|
||||
GFile *file,
|
||||
const GimpValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
GimpValueArray *return_vals;
|
||||
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
||||
GimpImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
INIT_I18N ();
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
run_mode = param[0].data.d_int32;
|
||||
image = load_image (g_file_get_path (file), FALSE, &error);
|
||||
|
||||
*nreturn_vals = 1;
|
||||
*return_vals = values;
|
||||
if (! image)
|
||||
return gimp_procedure_new_return_values (procedure, status, error);
|
||||
|
||||
values[0].type = GIMP_PDB_STATUS;
|
||||
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
|
||||
return_vals = gimp_procedure_new_return_values (procedure,
|
||||
GIMP_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
GFile *file = g_file_new_for_uri (param[1].data.d_string);
|
||||
GIMP_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
image_ID = load_image (g_file_get_path (file), FALSE, &error);
|
||||
|
||||
if (image_ID != -1)
|
||||
{
|
||||
/* Return the new image that was loaded */
|
||||
*nreturn_vals = 2;
|
||||
values[1].type = GIMP_PDB_IMAGE;
|
||||
values[1].data.d_image = image_ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = GIMP_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
}
|
||||
else if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
GFile *file = g_file_new_for_uri (param[3].data.d_string);
|
||||
GimpMetadata *metadata = NULL;
|
||||
GimpMetadataSaveFlags metadata_flags;
|
||||
WebPSaveParams params;
|
||||
GimpExportReturn export = GIMP_EXPORT_CANCEL;
|
||||
|
||||
if (run_mode == GIMP_RUN_INTERACTIVE ||
|
||||
run_mode == GIMP_RUN_WITH_LAST_VALS)
|
||||
gimp_ui_init (PLUG_IN_BINARY, FALSE);
|
||||
|
||||
image_ID = param[1].data.d_int32;
|
||||
drawable_ID = param[2].data.d_int32;
|
||||
|
||||
/* Default settings */
|
||||
params.preset = WEBP_PRESET_DEFAULT;
|
||||
params.lossless = FALSE;
|
||||
params.animation = FALSE;
|
||||
params.loop = TRUE;
|
||||
params.minimize_size = TRUE;
|
||||
params.kf_distance = 50;
|
||||
params.quality = 90.0f;
|
||||
params.alpha_quality = 100.0f;
|
||||
params.exif = FALSE;
|
||||
params.iptc = FALSE;
|
||||
params.xmp = FALSE;
|
||||
params.delay = 200;
|
||||
params.force_delay = FALSE;
|
||||
|
||||
/* Override the defaults with preferences. */
|
||||
metadata = gimp_image_metadata_save_prepare (image_ID,
|
||||
"image/webp",
|
||||
&metadata_flags);
|
||||
params.exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
|
||||
params.xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
|
||||
params.iptc = (metadata_flags & GIMP_METADATA_SAVE_IPTC) != 0;
|
||||
params.profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case GIMP_RUN_WITH_LAST_VALS:
|
||||
/* Possibly override with session data */
|
||||
gimp_get_data (SAVE_PROC, ¶ms);
|
||||
break;
|
||||
|
||||
case GIMP_RUN_INTERACTIVE:
|
||||
/* Possibly override with session data */
|
||||
gimp_get_data (SAVE_PROC, ¶ms);
|
||||
|
||||
if (! save_dialog (¶ms, image_ID))
|
||||
{
|
||||
status = GIMP_PDB_CANCEL;
|
||||
}
|
||||
break;
|
||||
|
||||
case GIMP_RUN_NONINTERACTIVE:
|
||||
if (nparams != 18)
|
||||
{
|
||||
status = GIMP_PDB_CALLING_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (param[5].data.d_int32 < WEBP_PRESET_DEFAULT ||
|
||||
param[5].data.d_int32 > WEBP_PRESET_TEXT)
|
||||
params.preset = WEBP_PRESET_DEFAULT;
|
||||
else
|
||||
params.preset = param[5].data.d_int32;
|
||||
|
||||
params.lossless = param[6].data.d_int32;
|
||||
params.quality = param[7].data.d_float;
|
||||
params.alpha_quality = param[8].data.d_float;
|
||||
params.animation = param[9].data.d_int32;
|
||||
params.loop = param[10].data.d_int32;
|
||||
params.minimize_size = param[11].data.d_int32;
|
||||
params.kf_distance = param[12].data.d_int32;
|
||||
params.exif = param[13].data.d_int32;
|
||||
params.iptc = param[14].data.d_int32;
|
||||
params.xmp = param[15].data.d_int32;
|
||||
params.delay = param[16].data.d_int32;
|
||||
params.force_delay = param[17].data.d_int32;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == GIMP_PDB_SUCCESS && (run_mode == GIMP_RUN_INTERACTIVE ||
|
||||
run_mode == GIMP_RUN_WITH_LAST_VALS))
|
||||
{
|
||||
GimpExportCapabilities capabilities =
|
||||
GIMP_EXPORT_CAN_HANDLE_RGB |
|
||||
GIMP_EXPORT_CAN_HANDLE_GRAY |
|
||||
GIMP_EXPORT_CAN_HANDLE_INDEXED |
|
||||
GIMP_EXPORT_CAN_HANDLE_ALPHA;
|
||||
|
||||
if (params.animation)
|
||||
capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION;
|
||||
|
||||
export = gimp_export_image (&image_ID, &drawable_ID, "WebP",
|
||||
capabilities);
|
||||
|
||||
if (export == GIMP_EXPORT_CANCEL)
|
||||
{
|
||||
values[0].data.d_status = GIMP_PDB_CANCEL;
|
||||
status = GIMP_PDB_CANCEL;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == GIMP_PDB_SUCCESS)
|
||||
{
|
||||
if (! save_image (g_file_get_path (file),
|
||||
image_ID,
|
||||
drawable_ID,
|
||||
metadata, metadata_flags,
|
||||
¶ms,
|
||||
&error))
|
||||
{
|
||||
status = GIMP_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (export == GIMP_EXPORT_EXPORT)
|
||||
gimp_image_delete (image_ID);
|
||||
|
||||
if (metadata)
|
||||
g_object_unref (metadata);
|
||||
|
||||
if (status == GIMP_PDB_SUCCESS)
|
||||
{
|
||||
/* save parameters for later */
|
||||
gimp_set_data (SAVE_PROC, ¶ms, sizeof (params));
|
||||
}
|
||||
}
|
||||
|
||||
/* If an error was supplied, include it in the return values */
|
||||
if (status != GIMP_PDB_SUCCESS && error)
|
||||
{
|
||||
*nreturn_vals = 2;
|
||||
values[1].type = GIMP_PDB_STRING;
|
||||
values[1].data.d_string = error->message;
|
||||
}
|
||||
|
||||
values[0].data.d_status = status;
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static GimpValueArray *
|
||||
webp_save (GimpProcedure *procedure,
|
||||
GimpRunMode run_mode,
|
||||
GimpImage *image,
|
||||
GimpDrawable *drawable,
|
||||
GFile *file,
|
||||
const GimpValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
||||
GimpMetadata *metadata = NULL;
|
||||
GimpMetadataSaveFlags metadata_flags;
|
||||
WebPSaveParams params;
|
||||
GimpExportReturn export = GIMP_EXPORT_CANCEL;
|
||||
GError *error = NULL;
|
||||
|
||||
INIT_I18N ();
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (run_mode == GIMP_RUN_INTERACTIVE ||
|
||||
run_mode == GIMP_RUN_WITH_LAST_VALS)
|
||||
gimp_ui_init (PLUG_IN_BINARY, FALSE);
|
||||
|
||||
/* Default settings */
|
||||
params.preset = WEBP_PRESET_DEFAULT;
|
||||
params.lossless = FALSE;
|
||||
params.animation = FALSE;
|
||||
params.loop = TRUE;
|
||||
params.minimize_size = TRUE;
|
||||
params.kf_distance = 50;
|
||||
params.quality = 90.0f;
|
||||
params.alpha_quality = 100.0f;
|
||||
params.exif = FALSE;
|
||||
params.iptc = FALSE;
|
||||
params.xmp = FALSE;
|
||||
params.delay = 200;
|
||||
params.force_delay = FALSE;
|
||||
|
||||
/* Override the defaults with preferences. */
|
||||
metadata = gimp_image_metadata_save_prepare (image,
|
||||
"image/webp",
|
||||
&metadata_flags);
|
||||
params.exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
|
||||
params.xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
|
||||
params.iptc = (metadata_flags & GIMP_METADATA_SAVE_IPTC) != 0;
|
||||
params.profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case GIMP_RUN_WITH_LAST_VALS:
|
||||
/* Possibly override with session data */
|
||||
gimp_get_data (SAVE_PROC, ¶ms);
|
||||
break;
|
||||
|
||||
case GIMP_RUN_INTERACTIVE:
|
||||
/* Possibly override with session data */
|
||||
gimp_get_data (SAVE_PROC, ¶ms);
|
||||
|
||||
if (! save_dialog (¶ms, image))
|
||||
return gimp_procedure_new_return_values (procedure,
|
||||
GIMP_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
case GIMP_RUN_NONINTERACTIVE:
|
||||
params.preset = GIMP_VALUES_GET_INT (args, 0);
|
||||
params.lossless = GIMP_VALUES_GET_BOOLEAN (args, 1);
|
||||
params.quality = GIMP_VALUES_GET_DOUBLE (args, 2);
|
||||
params.alpha_quality = GIMP_VALUES_GET_DOUBLE (args, 3);
|
||||
params.animation = GIMP_VALUES_GET_BOOLEAN (args, 4);
|
||||
params.loop = GIMP_VALUES_GET_BOOLEAN (args, 5);
|
||||
params.minimize_size = GIMP_VALUES_GET_BOOLEAN (args, 6);
|
||||
params.kf_distance = GIMP_VALUES_GET_INT (args, 7);
|
||||
params.exif = GIMP_VALUES_GET_BOOLEAN (args, 8);
|
||||
params.iptc = GIMP_VALUES_GET_BOOLEAN (args, 9);
|
||||
params.xmp = GIMP_VALUES_GET_BOOLEAN (args, 10);
|
||||
params.delay = GIMP_VALUES_GET_INT (args, 11);
|
||||
params.force_delay = GIMP_VALUES_GET_BOOLEAN (args, 12);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (run_mode == GIMP_RUN_INTERACTIVE ||
|
||||
run_mode == GIMP_RUN_WITH_LAST_VALS)
|
||||
{
|
||||
GimpExportCapabilities capabilities = (GIMP_EXPORT_CAN_HANDLE_RGB |
|
||||
GIMP_EXPORT_CAN_HANDLE_GRAY |
|
||||
GIMP_EXPORT_CAN_HANDLE_INDEXED |
|
||||
GIMP_EXPORT_CAN_HANDLE_ALPHA);
|
||||
|
||||
if (params.animation)
|
||||
capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION;
|
||||
|
||||
export = gimp_export_image (&image, &drawable, "WebP",
|
||||
capabilities);
|
||||
|
||||
if (export == GIMP_EXPORT_CANCEL)
|
||||
return gimp_procedure_new_return_values (procedure,
|
||||
GIMP_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (! save_image (g_file_get_path (file),
|
||||
image,
|
||||
drawable,
|
||||
metadata, metadata_flags,
|
||||
¶ms,
|
||||
&error))
|
||||
{
|
||||
status = GIMP_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
|
||||
if (export == GIMP_EXPORT_EXPORT)
|
||||
gimp_image_delete (image);
|
||||
|
||||
if (metadata)
|
||||
g_object_unref (metadata);
|
||||
|
||||
if (status == GIMP_PDB_SUCCESS)
|
||||
{
|
||||
/* save parameters for later */
|
||||
gimp_set_data (SAVE_PROC, ¶ms, sizeof (params));
|
||||
}
|
||||
|
||||
return gimp_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue