Initial checkin of Pika from heckimp
This commit is contained in:
183
plug-ins/file-webp/file-webp-dialog.c
Normal file
183
plug-ins/file-webp/file-webp-dialog.c
Normal file
@ -0,0 +1,183 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* file-webp - WebP file format plug-in for the PIKA
|
||||
* Copyright (C) 2015 Nathan Osman
|
||||
* Copyright (C) 2016 Ben Touchette
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include <webp/encode.h>
|
||||
|
||||
#include "file-webp.h"
|
||||
#include "file-webp-dialog.h"
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
static void
|
||||
show_maxkeyframe_hints (GObject *config,
|
||||
const GParamSpec *pspec,
|
||||
GtkLabel *label)
|
||||
{
|
||||
gint kmax;
|
||||
|
||||
g_object_get (config,
|
||||
"keyframe-distance", &kmax,
|
||||
NULL);
|
||||
|
||||
if (kmax == 0)
|
||||
{
|
||||
gtk_label_set_text (label, _("(no keyframes)"));
|
||||
}
|
||||
else if (kmax == 1)
|
||||
{
|
||||
gtk_label_set_text (label, _("(all frames are keyframes)"));
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_label_set_text (label, "");
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
save_dialog (PikaImage *image,
|
||||
PikaProcedure *procedure,
|
||||
GObject *config)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkListStore *store;
|
||||
gint32 nlayers;
|
||||
gboolean animation_supported = FALSE;
|
||||
gboolean run;
|
||||
|
||||
g_free (pika_image_get_layers (image, &nlayers));
|
||||
|
||||
animation_supported = nlayers > 1;
|
||||
|
||||
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
image);
|
||||
|
||||
/* Create the combobox containing the presets */
|
||||
store = pika_int_store_new (_("Default"), WEBP_PRESET_DEFAULT,
|
||||
_("Picture"), WEBP_PRESET_PICTURE,
|
||||
_("Photo"), WEBP_PRESET_PHOTO,
|
||||
_("Drawing"), WEBP_PRESET_DRAWING,
|
||||
_("Icon"), WEBP_PRESET_ICON,
|
||||
_("Text"), WEBP_PRESET_TEXT,
|
||||
NULL);
|
||||
pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"preset", PIKA_INT_STORE (store));
|
||||
|
||||
/* Create scale for image and alpha quality */
|
||||
pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"quality", PIKA_TYPE_SPIN_SCALE);
|
||||
pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"alpha-quality", PIKA_TYPE_SPIN_SCALE);
|
||||
|
||||
/* Create frame for quality options */
|
||||
pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"quality-options",
|
||||
"quality", "alpha-quality",
|
||||
NULL);
|
||||
pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"quality-frame", "lossless", TRUE,
|
||||
"quality-options");
|
||||
|
||||
/* Create frame for additional features like Sharp YUV */
|
||||
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"advanced-title", _("Advanced Options"));
|
||||
|
||||
pika_procedure_dialog_set_sensitive (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"use-sharp-yuv",
|
||||
TRUE, config, "lossless", TRUE);
|
||||
pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"advanced-options",
|
||||
"use-sharp-yuv",
|
||||
NULL);
|
||||
|
||||
pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"advanced-frame", "advanced-title", FALSE,
|
||||
"advanced-options");
|
||||
|
||||
if (animation_supported)
|
||||
{
|
||||
GtkWidget *label_kf;
|
||||
|
||||
/* Hint for some special values of keyframe-distance. */
|
||||
label_kf = pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"keyframe-hint", NULL);
|
||||
gtk_label_set_xalign (GTK_LABEL (label_kf), 1.0);
|
||||
gtk_label_set_ellipsize (GTK_LABEL (label_kf), PANGO_ELLIPSIZE_END);
|
||||
pika_label_set_attributes (GTK_LABEL (label_kf),
|
||||
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
|
||||
-1);
|
||||
g_signal_connect (config, "notify::keyframe-distance",
|
||||
G_CALLBACK (show_maxkeyframe_hints),
|
||||
label_kf);
|
||||
show_maxkeyframe_hints (config, NULL, GTK_LABEL (label_kf));
|
||||
|
||||
/* when minimize-size is true, keyframe-distance and hint are insensitive. */
|
||||
pika_procedure_dialog_set_sensitive (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"keyframe-distance",
|
||||
TRUE, config, "minimize-size", TRUE);
|
||||
pika_procedure_dialog_set_sensitive (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"keyframe-hint",
|
||||
TRUE, config, "minimize-size", TRUE);
|
||||
|
||||
/* Create frame for animation options */
|
||||
pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"animation-options",
|
||||
"animation-loop",
|
||||
"minimize-size",
|
||||
"keyframe-distance",
|
||||
"keyframe-hint",
|
||||
"default-delay",
|
||||
"force-delay",
|
||||
NULL);
|
||||
pika_procedure_dialog_fill_expander (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"animation-frame", "animation", FALSE,
|
||||
"animation-options");
|
||||
|
||||
/* Fill dialog with containers*/
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"preset", "quality-frame",
|
||||
"advanced-frame", "animation-frame",
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fill dialog with containers*/
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"preset", "quality-frame", "advanced-frame",
|
||||
NULL);
|
||||
}
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
35
plug-ins/file-webp/file-webp-dialog.h
Normal file
35
plug-ins/file-webp/file-webp-dialog.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* file-webp - WebP file format plug-in for the PIKA
|
||||
* Copyright (C) 2015 Nathan Osman
|
||||
* Copyright (C) 2016 Ben Touchette
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __WEBP_DIALOG_H__
|
||||
#define __WEBP_DIALOG_H__
|
||||
|
||||
|
||||
gboolean save_dialog (PikaImage *image,
|
||||
PikaProcedure *procedure,
|
||||
GObject *config);
|
||||
|
||||
|
||||
#endif /* __WEBP_DIALOG_H__ */
|
293
plug-ins/file-webp/file-webp-load.c
Normal file
293
plug-ins/file-webp/file-webp-load.c
Normal file
@ -0,0 +1,293 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* file-webp - WebP file format plug-in for the PIKA
|
||||
* Copyright (C) 2015 Nathan Osman
|
||||
* Copyright (C) 2016 Ben Touchette
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <webp/decode.h>
|
||||
#include <webp/demux.h>
|
||||
#include <webp/mux.h>
|
||||
|
||||
#include <gegl.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "file-webp-load.h"
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
static void
|
||||
create_layer (PikaImage *image,
|
||||
uint8_t *layer_data,
|
||||
gint32 position,
|
||||
gchar *name,
|
||||
gint width,
|
||||
gint height)
|
||||
{
|
||||
PikaLayer *layer;
|
||||
GeglBuffer *buffer;
|
||||
GeglRectangle extent;
|
||||
|
||||
layer = pika_layer_new (image, name,
|
||||
width, height,
|
||||
PIKA_RGBA_IMAGE,
|
||||
100,
|
||||
pika_image_get_default_new_layer_mode (image));
|
||||
|
||||
pika_image_insert_layer (image, layer, NULL, position);
|
||||
|
||||
/* Retrieve the buffer for the layer */
|
||||
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
|
||||
|
||||
/* Copy the image data to the region */
|
||||
gegl_rectangle_set (&extent, 0, 0, width, height);
|
||||
gegl_buffer_set (buffer, &extent, 0, NULL, layer_data,
|
||||
GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
/* Flush the drawable and detach */
|
||||
gegl_buffer_flush (buffer);
|
||||
g_object_unref (buffer);
|
||||
}
|
||||
|
||||
PikaImage *
|
||||
load_image (GFile *file,
|
||||
gboolean interactive,
|
||||
GError **error)
|
||||
{
|
||||
uint8_t *indata = NULL;
|
||||
gsize indatalen;
|
||||
gint width;
|
||||
gint height;
|
||||
PikaImage *image;
|
||||
WebPMux *mux;
|
||||
WebPData wp_data;
|
||||
PikaColorProfile *profile = NULL;
|
||||
uint32_t flags;
|
||||
gboolean animation = FALSE;
|
||||
gboolean icc = FALSE;
|
||||
gboolean exif = FALSE;
|
||||
gboolean xmp = FALSE;
|
||||
|
||||
/* Attempt to read the file contents from disk */
|
||||
if (! g_file_get_contents (g_file_peek_path (file),
|
||||
(gchar **) &indata,
|
||||
&indatalen,
|
||||
error))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Validate WebP data */
|
||||
if (! WebPGetInfo (indata, indatalen, &width, &height))
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, 0,
|
||||
_("Invalid WebP file '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wp_data.bytes = indata;
|
||||
wp_data.size = indatalen;
|
||||
|
||||
mux = WebPMuxCreate (&wp_data, 1);
|
||||
if (! mux)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WebPMuxGetFeatures (mux, &flags);
|
||||
|
||||
if (flags & ANIMATION_FLAG)
|
||||
animation = TRUE;
|
||||
|
||||
if (flags & ICCP_FLAG)
|
||||
icc = TRUE;
|
||||
|
||||
if (flags & EXIF_FLAG)
|
||||
exif = TRUE;
|
||||
|
||||
if (flags & XMP_FLAG)
|
||||
xmp = TRUE;
|
||||
|
||||
/* TODO: decode the image in "chunks" or "tiles" */
|
||||
/* TODO: check if an alpha channel is present */
|
||||
|
||||
/* Create the new image and associated layer */
|
||||
image = pika_image_new (width, height, PIKA_RGB);
|
||||
|
||||
if (icc)
|
||||
{
|
||||
WebPData icc_profile;
|
||||
|
||||
WebPMuxGetChunk (mux, "ICCP", &icc_profile);
|
||||
profile = pika_color_profile_new_from_icc_profile (icc_profile.bytes,
|
||||
icc_profile.size, NULL);
|
||||
if (profile)
|
||||
pika_image_set_color_profile (image, profile);
|
||||
}
|
||||
|
||||
if (! animation)
|
||||
{
|
||||
uint8_t *outdata;
|
||||
|
||||
/* Attempt to decode the data as a WebP image */
|
||||
outdata = WebPDecodeRGBA (indata, indatalen, &width, &height);
|
||||
|
||||
/* Check to ensure the image data was loaded correctly */
|
||||
if (! outdata)
|
||||
{
|
||||
WebPMuxDelete (mux);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
create_layer (image, outdata, 0, _("Background"),
|
||||
width, height);
|
||||
|
||||
/* Free the image data */
|
||||
free (outdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
WebPAnimDecoder *dec = NULL;
|
||||
WebPAnimInfo anim_info;
|
||||
WebPAnimDecoderOptions dec_options;
|
||||
gint frame_num = 1;
|
||||
WebPDemuxer *demux = NULL;
|
||||
WebPIterator iter = { 0, };
|
||||
|
||||
if (! WebPAnimDecoderOptionsInit (&dec_options))
|
||||
{
|
||||
error:
|
||||
if (dec)
|
||||
WebPAnimDecoderDelete (dec);
|
||||
|
||||
if (demux)
|
||||
{
|
||||
WebPDemuxReleaseIterator (&iter);
|
||||
WebPDemuxDelete (demux);
|
||||
}
|
||||
|
||||
WebPMuxDelete (mux);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* dec_options.color_mode is MODE_RGBA by default here */
|
||||
dec = WebPAnimDecoderNew (&wp_data, &dec_options);
|
||||
if (! dec)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, 0,
|
||||
_("Failed to decode animated WebP file '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (! WebPAnimDecoderGetInfo (dec, &anim_info))
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, 0,
|
||||
_("Failed to decode animated WebP information from '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
goto error;
|
||||
}
|
||||
|
||||
demux = WebPDemux (&wp_data);
|
||||
if (! demux || ! WebPDemuxGetFrame (demux, 1, &iter))
|
||||
goto error;
|
||||
|
||||
/* Attempt to decode the data as a WebP animation image */
|
||||
while (WebPAnimDecoderHasMoreFrames (dec))
|
||||
{
|
||||
uint8_t *outdata;
|
||||
int timestamp;
|
||||
gchar *name;
|
||||
|
||||
if (! WebPAnimDecoderGetNext (dec, &outdata, ×tamp))
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, 0,
|
||||
_("Failed to decode animated WebP frame from '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
goto error;
|
||||
}
|
||||
|
||||
name = g_strdup_printf (_("Frame %d (%dms)"), frame_num, iter.duration);
|
||||
create_layer (image, outdata, 0, name, width, height);
|
||||
g_free (name);
|
||||
|
||||
frame_num++;
|
||||
WebPDemuxNextFrame (&iter);
|
||||
}
|
||||
|
||||
WebPAnimDecoderDelete (dec);
|
||||
WebPDemuxReleaseIterator (&iter);
|
||||
WebPDemuxDelete (demux);
|
||||
}
|
||||
|
||||
/* Free the original compressed data */
|
||||
g_free (indata);
|
||||
|
||||
if (exif || xmp)
|
||||
{
|
||||
PikaMetadata *metadata;
|
||||
|
||||
if (exif)
|
||||
{
|
||||
WebPData exif;
|
||||
|
||||
WebPMuxGetChunk (mux, "EXIF", &exif);
|
||||
}
|
||||
|
||||
if (xmp)
|
||||
{
|
||||
WebPData xmp;
|
||||
|
||||
WebPMuxGetChunk (mux, "XMP ", &xmp);
|
||||
}
|
||||
|
||||
metadata = pika_image_metadata_load_prepare (image, "image/webp",
|
||||
file, NULL);
|
||||
if (metadata)
|
||||
{
|
||||
PikaMetadataLoadFlags flags = PIKA_METADATA_LOAD_ALL;
|
||||
|
||||
if (profile)
|
||||
flags &= ~PIKA_METADATA_LOAD_COLORSPACE;
|
||||
|
||||
pika_image_metadata_load_finish (image, "image/webp",
|
||||
metadata, flags);
|
||||
g_object_unref (metadata);
|
||||
}
|
||||
}
|
||||
|
||||
WebPMuxDelete (mux);
|
||||
|
||||
if (profile)
|
||||
g_object_unref (profile);
|
||||
|
||||
return image;
|
||||
}
|
35
plug-ins/file-webp/file-webp-load.h
Normal file
35
plug-ins/file-webp/file-webp-load.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* file-webp - WebP file format plug-in for the PIKA
|
||||
* Copyright (C) 2015 Nathan Osman
|
||||
* Copyright (C) 2016 Ben Touchette
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __WEBP_LOAD_H__
|
||||
#define __WEBP_LOAD_H__
|
||||
|
||||
|
||||
PikaImage * load_image (GFile *file,
|
||||
gboolean interactive,
|
||||
GError **error);
|
||||
|
||||
|
||||
#endif /* __WEBP_LOAD_H__ */
|
912
plug-ins/file-webp/file-webp-save.c
Normal file
912
plug-ins/file-webp/file-webp-save.c
Normal file
@ -0,0 +1,912 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* file-webp - WebP file format plug-in for the PIKA
|
||||
* Copyright (C) 2015 Nathan Osman
|
||||
* Copyright (C) 2016 Ben Touchette
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <gegl.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include <webp/encode.h>
|
||||
#include <webp/mux.h>
|
||||
|
||||
#include "file-webp-save.h"
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
int webp_anim_file_writer (FILE *outfile,
|
||||
const uint8_t *data,
|
||||
size_t data_size);
|
||||
int webp_file_writer (const uint8_t *data,
|
||||
size_t data_size,
|
||||
const WebPPicture *picture);
|
||||
int webp_file_progress (int percent,
|
||||
const WebPPicture *picture);
|
||||
gchar * webp_error_string (WebPEncodingError error_code);
|
||||
|
||||
static void webp_decide_output (PikaImage *image,
|
||||
GObject *config,
|
||||
PikaColorProfile **profile,
|
||||
gboolean *out_linear);
|
||||
|
||||
int
|
||||
webp_anim_file_writer (FILE *outfile,
|
||||
const uint8_t *data,
|
||||
size_t data_size)
|
||||
{
|
||||
int ok = 0;
|
||||
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
|
||||
ok = (fwrite (data, data_size, 1, outfile) == 1);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int
|
||||
webp_file_writer (const uint8_t *data,
|
||||
size_t data_size,
|
||||
const WebPPicture *picture)
|
||||
{
|
||||
FILE *outfile;
|
||||
|
||||
/* Obtain the FILE* and write the data to the file */
|
||||
outfile = (FILE *) picture->custom_ptr;
|
||||
|
||||
return fwrite (data, sizeof (uint8_t), data_size, outfile) == data_size;
|
||||
}
|
||||
|
||||
int
|
||||
webp_file_progress (int percent,
|
||||
const WebPPicture *picture)
|
||||
{
|
||||
return pika_progress_update (percent / 100.0);
|
||||
}
|
||||
|
||||
gchar *
|
||||
webp_error_string (WebPEncodingError error_code)
|
||||
{
|
||||
switch (error_code)
|
||||
{
|
||||
case VP8_ENC_ERROR_OUT_OF_MEMORY:
|
||||
return g_strdup (_("out of memory"));
|
||||
case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
|
||||
return g_strdup (_("not enough memory to flush bits"));
|
||||
case VP8_ENC_ERROR_NULL_PARAMETER:
|
||||
return g_strdup (_("NULL parameter"));
|
||||
case VP8_ENC_ERROR_INVALID_CONFIGURATION:
|
||||
return g_strdup (_("invalid configuration"));
|
||||
case VP8_ENC_ERROR_BAD_DIMENSION:
|
||||
/* TRANSLATORS: widthxheight with UTF-8 encoded multiply sign. */
|
||||
return g_strdup_printf (_("bad image dimensions (maximum: %d\xc3\x97%d)"),
|
||||
WEBP_MAX_DIMENSION, WEBP_MAX_DIMENSION);
|
||||
case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
|
||||
return g_strdup (_("partition is bigger than 512K"));
|
||||
case VP8_ENC_ERROR_PARTITION_OVERFLOW:
|
||||
return g_strdup (_("partition is bigger than 16M"));
|
||||
case VP8_ENC_ERROR_BAD_WRITE:
|
||||
return g_strdup (_("unable to flush bytes"));
|
||||
case VP8_ENC_ERROR_FILE_TOO_BIG:
|
||||
return g_strdup (_("file is larger than 4GiB"));
|
||||
case VP8_ENC_ERROR_USER_ABORT:
|
||||
return g_strdup (_("user aborted encoding"));
|
||||
case VP8_ENC_ERROR_LAST:
|
||||
return g_strdup (_("list terminator"));
|
||||
default:
|
||||
return g_strdup (_("unknown error"));
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
save_layer (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GObject *config,
|
||||
GError **error)
|
||||
{
|
||||
gboolean status = FALSE;
|
||||
FILE *outfile = NULL;
|
||||
WebPConfig webp_config = { 0, };
|
||||
WebPPicture picture = { 0, };
|
||||
guchar *buffer = NULL;
|
||||
gint w, h;
|
||||
gboolean has_alpha;
|
||||
const gchar *encoding;
|
||||
const Babl *format;
|
||||
const Babl *space = NULL;
|
||||
gint bpp;
|
||||
PikaColorProfile *profile = NULL;
|
||||
GeglBuffer *geglbuffer = NULL;
|
||||
GeglRectangle extent;
|
||||
gchar *indata;
|
||||
gsize indatalen;
|
||||
struct stat stsz;
|
||||
int fd_outfile;
|
||||
WebPData chunk;
|
||||
gboolean out_linear = FALSE;
|
||||
int res;
|
||||
WebPPreset preset;
|
||||
gboolean lossless;
|
||||
gdouble quality;
|
||||
gdouble alpha_quality;
|
||||
gboolean use_sharp_yuv;
|
||||
|
||||
g_object_get (config,
|
||||
"preset", &preset,
|
||||
"lossless", &lossless,
|
||||
"quality", &quality,
|
||||
"alpha-quality", &alpha_quality,
|
||||
"use-sharp-yuv", &use_sharp_yuv,
|
||||
NULL);
|
||||
|
||||
webp_decide_output (image, config, &profile, &out_linear);
|
||||
if (profile)
|
||||
{
|
||||
space = pika_color_profile_get_space (profile,
|
||||
PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
|
||||
error);
|
||||
if (error && *error)
|
||||
{
|
||||
/* Don't make this a hard failure yet still output the error.
|
||||
*/
|
||||
g_printerr ("%s: error getting the profile space: %s",
|
||||
G_STRFUNC, (*error)->message);
|
||||
g_clear_error (error);
|
||||
}
|
||||
|
||||
}
|
||||
if (! space)
|
||||
space = pika_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
|
||||
* cleanup
|
||||
*/
|
||||
|
||||
do
|
||||
{
|
||||
/* Begin displaying export progress */
|
||||
pika_progress_init_printf (_("Saving '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
|
||||
/* Attempt to open the output file */
|
||||
outfile = g_fopen (g_file_peek_path (file), "w+b");
|
||||
|
||||
if (! outfile)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR,
|
||||
g_file_error_from_errno (errno),
|
||||
_("Unable to open '%s' for writing: %s"),
|
||||
pika_file_get_utf8_name (file),
|
||||
g_strerror (errno));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Obtain the drawable type */
|
||||
has_alpha = pika_drawable_has_alpha (drawable);
|
||||
|
||||
if (has_alpha)
|
||||
{
|
||||
if (out_linear)
|
||||
encoding = "RGBA u8";
|
||||
else
|
||||
encoding = "R'G'B'A u8";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (out_linear)
|
||||
encoding = "RGB u8";
|
||||
else
|
||||
encoding = "R'G'B' u8";
|
||||
}
|
||||
|
||||
format = babl_format_with_space (encoding, space);
|
||||
bpp = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
/* Retrieve the buffer for the layer */
|
||||
geglbuffer = pika_drawable_get_buffer (drawable);
|
||||
extent = *gegl_buffer_get_extent (geglbuffer);
|
||||
w = extent.width;
|
||||
h = extent.height;
|
||||
|
||||
/* Initialize the WebP configuration with a preset and fill in the
|
||||
* remaining values */
|
||||
WebPConfigPreset (&webp_config, preset, quality);
|
||||
|
||||
webp_config.lossless = lossless;
|
||||
webp_config.method = 6; /* better quality */
|
||||
webp_config.alpha_quality = alpha_quality;
|
||||
webp_config.use_sharp_yuv = use_sharp_yuv ? 1 : 0;
|
||||
|
||||
/* Prepare the WebP structure */
|
||||
WebPPictureInit (&picture);
|
||||
picture.use_argb = 1;
|
||||
picture.width = w;
|
||||
picture.height = h;
|
||||
picture.writer = webp_file_writer;
|
||||
picture.custom_ptr = outfile;
|
||||
picture.progress_hook = webp_file_progress;
|
||||
|
||||
/* Attempt to allocate a buffer of the appropriate size */
|
||||
buffer = g_try_malloc (w * h * bpp);
|
||||
if (! buffer)
|
||||
break;
|
||||
|
||||
/* Read the region into the buffer */
|
||||
gegl_buffer_get (geglbuffer, &extent, 1.0, format, buffer,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
/* Use the appropriate function to import the data from the buffer */
|
||||
if (! has_alpha)
|
||||
{
|
||||
status = WebPPictureImportRGB (&picture, buffer, w * bpp);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = WebPPictureImportRGBA (&picture, buffer, w * bpp);
|
||||
}
|
||||
|
||||
g_free (buffer);
|
||||
if (! status)
|
||||
{
|
||||
g_printerr ("%s: memory error in WebPPictureImportRGB(A)().",
|
||||
G_STRFUNC);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Perform the actual encode */
|
||||
if (! WebPEncode (&webp_config, &picture))
|
||||
{
|
||||
gchar *error_str = webp_error_string (picture.error_code);
|
||||
|
||||
g_printerr ("WebP error: '%s'", error_str);
|
||||
g_set_error (error, G_FILE_ERROR,
|
||||
picture.error_code,
|
||||
_("WebP error: '%s'"),
|
||||
error_str);
|
||||
g_free (error_str);
|
||||
status = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* The cleanup stuff still needs to run but indicate that everything
|
||||
* completed successfully
|
||||
*/
|
||||
status = TRUE;
|
||||
|
||||
}
|
||||
while (0);
|
||||
|
||||
/* Flush the drawable and detach */
|
||||
if (geglbuffer)
|
||||
{
|
||||
g_object_unref (geglbuffer);
|
||||
}
|
||||
|
||||
fflush (outfile);
|
||||
fd_outfile = fileno (outfile);
|
||||
fstat (fd_outfile, &stsz);
|
||||
indatalen = stsz.st_size;
|
||||
if (indatalen > 0)
|
||||
{
|
||||
indata = (gchar*) g_malloc (indatalen);
|
||||
rewind (outfile);
|
||||
res = fread (indata, 1, indatalen, outfile);
|
||||
if (res > 0)
|
||||
{
|
||||
WebPMux *mux;
|
||||
WebPData wp_data;
|
||||
|
||||
wp_data.bytes = (uint8_t*) indata;
|
||||
wp_data.size = indatalen;
|
||||
mux = WebPMuxCreate (&wp_data, 1);
|
||||
|
||||
if (mux)
|
||||
{
|
||||
/* Save ICC data */
|
||||
if (profile)
|
||||
{
|
||||
const guint8 *icc_data;
|
||||
gsize icc_data_size;
|
||||
|
||||
icc_data = pika_color_profile_get_icc_profile (profile,
|
||||
&icc_data_size);
|
||||
chunk.bytes = icc_data;
|
||||
chunk.size = icc_data_size;
|
||||
WebPMuxSetChunk(mux, "ICCP", &chunk, 1);
|
||||
|
||||
WebPMuxAssemble (mux, &wp_data);
|
||||
rewind (outfile);
|
||||
webp_anim_file_writer (outfile, wp_data.bytes, wp_data.size);
|
||||
}
|
||||
|
||||
WebPMuxDelete (mux);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("ERROR: Cannot create mux. Can't save features update.\n");
|
||||
}
|
||||
|
||||
WebPDataClear (&wp_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("ERROR: No data read for features. Can't save features update.\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("ERROR: No data for features. Can't save features update.\n");
|
||||
}
|
||||
|
||||
/* Free any resources */
|
||||
if (outfile)
|
||||
fclose (outfile);
|
||||
|
||||
WebPPictureFree (&picture);
|
||||
g_clear_object (&profile);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static gint
|
||||
parse_ms_tag (const gchar *str)
|
||||
{
|
||||
gint sum = 0;
|
||||
gint offset = 0;
|
||||
gint length;
|
||||
|
||||
length = strlen (str);
|
||||
|
||||
find_another_bra:
|
||||
|
||||
while ((offset < length) && (str[offset] != '('))
|
||||
offset++;
|
||||
|
||||
if (offset >= length)
|
||||
return -1;
|
||||
|
||||
if (! g_ascii_isdigit (str[++offset]))
|
||||
goto find_another_bra;
|
||||
|
||||
do
|
||||
{
|
||||
sum *= 10;
|
||||
sum += str[offset] - '0';
|
||||
offset++;
|
||||
}
|
||||
while ((offset < length) && (g_ascii_isdigit (str[offset])));
|
||||
|
||||
if (length - offset <= 2)
|
||||
return -3;
|
||||
|
||||
if ((g_ascii_toupper (str[offset]) != 'M') ||
|
||||
(g_ascii_toupper (str[offset + 1]) != 'S'))
|
||||
return -4;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
static gint
|
||||
get_layer_delay (PikaLayer *layer)
|
||||
{
|
||||
gchar *layer_name;
|
||||
gint delay_ms;
|
||||
|
||||
layer_name = pika_item_get_name (PIKA_ITEM (layer));
|
||||
delay_ms = parse_ms_tag (layer_name);
|
||||
g_free (layer_name);
|
||||
|
||||
return delay_ms;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_combine (const char* str)
|
||||
{
|
||||
gint offset = 0;
|
||||
gint length = strlen (str);
|
||||
|
||||
while ((offset + 9) <= length)
|
||||
{
|
||||
if (strncmp (&str[offset], "(combine)", 9) == 0)
|
||||
return TRUE;
|
||||
|
||||
if (strncmp (&str[offset], "(replace)", 9) == 0)
|
||||
return FALSE;
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
get_layer_needs_combine (PikaLayer *layer)
|
||||
{
|
||||
gchar *layer_name;
|
||||
gboolean needs_combine;
|
||||
|
||||
layer_name = pika_item_get_name (PIKA_ITEM (layer));
|
||||
needs_combine = parse_combine (layer_name);
|
||||
g_free (layer_name);
|
||||
|
||||
return needs_combine;
|
||||
}
|
||||
|
||||
static GeglBuffer *
|
||||
combine_buffers (GeglBuffer *layer_buffer,
|
||||
GeglBuffer *prev_frame_buffer)
|
||||
{
|
||||
GeglBuffer *buffer;
|
||||
GeglNode *graph;
|
||||
GeglNode *source;
|
||||
GeglNode *backdrop;
|
||||
GeglNode *over;
|
||||
GeglNode *target;
|
||||
|
||||
graph = gegl_node_new ();
|
||||
buffer = gegl_buffer_new (gegl_buffer_get_extent (prev_frame_buffer),
|
||||
gegl_buffer_get_format (prev_frame_buffer));
|
||||
|
||||
source = gegl_node_new_child (graph,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", layer_buffer,
|
||||
NULL);
|
||||
backdrop = gegl_node_new_child (graph,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", prev_frame_buffer,
|
||||
NULL);
|
||||
|
||||
over = gegl_node_new_child (graph,
|
||||
"operation", "gegl:over",
|
||||
NULL);
|
||||
target = gegl_node_new_child (graph,
|
||||
"operation", "gegl:write-buffer",
|
||||
"buffer", buffer,
|
||||
NULL);
|
||||
gegl_node_link_many (backdrop, over, target, NULL);
|
||||
gegl_node_connect (source, "output", over, "aux");
|
||||
gegl_node_process (target);
|
||||
g_object_unref (graph);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
gboolean
|
||||
save_animation (GFile *file,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GObject *config,
|
||||
GError **error)
|
||||
{
|
||||
GList *layers;
|
||||
gint32 n_layers;
|
||||
gboolean status = TRUE;
|
||||
FILE *outfile = NULL;
|
||||
guchar *buffer = NULL;
|
||||
gint buffer_size = 0;
|
||||
gint w, h;
|
||||
gint bpp;
|
||||
gboolean has_alpha;
|
||||
const gchar *encoding;
|
||||
const Babl *format;
|
||||
const Babl *space = NULL;
|
||||
PikaColorProfile *profile = NULL;
|
||||
WebPAnimEncoderOptions enc_options;
|
||||
WebPData webp_data;
|
||||
int frame_timestamp = 0;
|
||||
WebPAnimEncoder *enc = NULL;
|
||||
GeglBuffer *prev_frame = NULL;
|
||||
gboolean out_linear = FALSE;
|
||||
WebPPreset preset;
|
||||
gboolean lossless;
|
||||
gboolean animation;
|
||||
gboolean loop;
|
||||
gboolean minimize_size;
|
||||
gint keyframe_distance;
|
||||
gdouble quality;
|
||||
gdouble alpha_quality;
|
||||
gint default_delay;
|
||||
gboolean force_delay;
|
||||
gboolean use_sharp_yuv;
|
||||
|
||||
g_return_val_if_fail (n_drawables > 0, FALSE);
|
||||
|
||||
g_object_get (config,
|
||||
"preset", &preset,
|
||||
"lossless", &lossless,
|
||||
"animation", &animation,
|
||||
"animation-loop", &loop,
|
||||
"minimize-size", &minimize_size,
|
||||
"keyframe-distance", &keyframe_distance,
|
||||
"quality", &quality,
|
||||
"alpha-quality", &alpha_quality,
|
||||
"default-delay", &default_delay,
|
||||
"force-delay", &force_delay,
|
||||
"use-sharp-yuv", &use_sharp_yuv,
|
||||
NULL);
|
||||
|
||||
layers = pika_image_list_layers (image);
|
||||
|
||||
if (! layers)
|
||||
return FALSE;
|
||||
|
||||
layers = g_list_reverse (layers);
|
||||
n_layers = g_list_length (layers);
|
||||
|
||||
webp_decide_output (image, config, &profile, &out_linear);
|
||||
if (profile)
|
||||
{
|
||||
space = pika_color_profile_get_space (profile,
|
||||
PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
|
||||
error);
|
||||
if (error && *error)
|
||||
{
|
||||
/* Don't make this a hard failure yet still output the error.
|
||||
*/
|
||||
g_printerr ("%s: error getting the profile space: %s",
|
||||
G_STRFUNC, (*error)->message);
|
||||
g_clear_error (error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! space)
|
||||
space = pika_drawable_get_format (drawables[0]);
|
||||
|
||||
pika_image_undo_freeze (image);
|
||||
|
||||
WebPDataInit (&webp_data);
|
||||
|
||||
do
|
||||
{
|
||||
GList *list;
|
||||
gint i;
|
||||
|
||||
/* Begin displaying export progress */
|
||||
pika_progress_init_printf (_("Saving '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
|
||||
/* Attempt to open the output file */
|
||||
outfile = g_fopen (g_file_peek_path (file), "wb");
|
||||
|
||||
if (! outfile)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR,
|
||||
g_file_error_from_errno (errno),
|
||||
_("Unable to open '%s' for writing: %s"),
|
||||
pika_file_get_utf8_name (file),
|
||||
g_strerror (errno));
|
||||
status = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (! WebPAnimEncoderOptionsInit (&enc_options))
|
||||
{
|
||||
g_printerr ("ERROR: version mismatch\n");
|
||||
status = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
enc_options.anim_params.loop_count = 0;
|
||||
if (! loop)
|
||||
enc_options.anim_params.loop_count = 1;
|
||||
|
||||
enc_options.allow_mixed = lossless ? 0 : 1;
|
||||
enc_options.minimize_size = minimize_size ? 1 : 0;
|
||||
if (! minimize_size)
|
||||
{
|
||||
enc_options.kmax = keyframe_distance;
|
||||
/* explicitly force minimum key-frame distance too, for good measure */
|
||||
enc_options.kmin = keyframe_distance - 1;
|
||||
}
|
||||
|
||||
for (list = layers, i = 0;
|
||||
list;
|
||||
list = g_list_next (list), i++)
|
||||
{
|
||||
GeglBuffer *geglbuffer;
|
||||
GeglBuffer *current_frame;
|
||||
GeglRectangle extent;
|
||||
WebPConfig webp_config;
|
||||
WebPPicture picture;
|
||||
WebPMemoryWriter mw = { 0 };
|
||||
PikaDrawable *drawable = list->data;
|
||||
gint delay;
|
||||
gboolean needs_combine;
|
||||
|
||||
delay = get_layer_delay (PIKA_LAYER (drawable));
|
||||
needs_combine = get_layer_needs_combine (PIKA_LAYER (drawable));
|
||||
|
||||
/* Obtain the drawable type */
|
||||
has_alpha = pika_drawable_has_alpha (drawable);
|
||||
|
||||
if (has_alpha)
|
||||
{
|
||||
if (out_linear)
|
||||
encoding = "RGBA u8";
|
||||
else
|
||||
encoding = "R'G'B'A u8";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (out_linear)
|
||||
encoding = "RGB u8";
|
||||
else
|
||||
encoding = "R'G'B' u8";
|
||||
}
|
||||
|
||||
format = babl_format_with_space (encoding, space);
|
||||
bpp = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
/* fix layers to avoid offset errors */
|
||||
pika_layer_resize_to_image_size (PIKA_LAYER (drawable));
|
||||
|
||||
/* Retrieve the buffer for the layer */
|
||||
geglbuffer = pika_drawable_get_buffer (drawable);
|
||||
extent = *gegl_buffer_get_extent (geglbuffer);
|
||||
w = extent.width;
|
||||
h = extent.height;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
enc = WebPAnimEncoderNew (w, h, &enc_options);
|
||||
if (! enc)
|
||||
{
|
||||
g_printerr ("ERROR: enc == null\n");
|
||||
status = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempt to allocate a buffer of the appropriate size */
|
||||
if (! buffer || buffer_size < w * h * bpp)
|
||||
{
|
||||
buffer = g_try_realloc (buffer, w * h * bpp);
|
||||
|
||||
if (! buffer)
|
||||
{
|
||||
g_printerr ("Buffer error: 'buffer null'\n");
|
||||
status = FALSE;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer_size = w * h * bpp;
|
||||
}
|
||||
}
|
||||
|
||||
WebPConfigPreset (&webp_config, preset, quality);
|
||||
|
||||
webp_config.lossless = lossless;
|
||||
webp_config.method = 6; /* better quality */
|
||||
webp_config.alpha_quality = alpha_quality;
|
||||
webp_config.exact = 1;
|
||||
webp_config.use_sharp_yuv = use_sharp_yuv ? 1 : 0;
|
||||
|
||||
WebPMemoryWriterInit (&mw);
|
||||
|
||||
/* Prepare the WebP structure */
|
||||
WebPPictureInit (&picture);
|
||||
picture.use_argb = 1;
|
||||
picture.argb_stride = w * bpp;
|
||||
picture.width = w;
|
||||
picture.height = h;
|
||||
picture.custom_ptr = &mw;
|
||||
picture.writer = WebPMemoryWrite;
|
||||
|
||||
if (i == 0 || ! needs_combine)
|
||||
{
|
||||
g_clear_object (&prev_frame);
|
||||
current_frame = geglbuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_frame = combine_buffers (geglbuffer, prev_frame);
|
||||
|
||||
/* release resources. */
|
||||
g_object_unref (geglbuffer);
|
||||
g_clear_object (&prev_frame);
|
||||
}
|
||||
prev_frame = current_frame;
|
||||
|
||||
/* Read the region into the buffer */
|
||||
gegl_buffer_get (current_frame, &extent, 1.0, format, buffer,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
/* Use the appropriate function to import the data from the buffer */
|
||||
if (! has_alpha)
|
||||
{
|
||||
status = WebPPictureImportRGB (&picture, buffer, w * bpp);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = WebPPictureImportRGBA (&picture, buffer, w * bpp);
|
||||
}
|
||||
|
||||
if (! status)
|
||||
{
|
||||
g_printerr ("%s: memory error in WebPPictureImportRGB(A)().",
|
||||
G_STRFUNC);
|
||||
}
|
||||
/* Perform the actual encode */
|
||||
else if (! WebPAnimEncoderAdd (enc, &picture, frame_timestamp,
|
||||
&webp_config))
|
||||
{
|
||||
gchar *error_str = webp_error_string (picture.error_code);
|
||||
g_printerr ("ERROR[%d]: line %d: %s\n",
|
||||
picture.error_code, __LINE__,
|
||||
error_str);
|
||||
g_free (error_str);
|
||||
status = FALSE;
|
||||
}
|
||||
|
||||
WebPMemoryWriterClear (&mw);
|
||||
WebPPictureFree (&picture);
|
||||
|
||||
if (status == FALSE)
|
||||
break;
|
||||
|
||||
pika_progress_update ((i + 1.0) / n_layers);
|
||||
frame_timestamp += (delay <= 0 || force_delay) ? default_delay : delay;
|
||||
}
|
||||
|
||||
g_free (buffer);
|
||||
|
||||
if (status == FALSE)
|
||||
break;
|
||||
|
||||
WebPAnimEncoderAdd (enc, NULL, frame_timestamp, NULL);
|
||||
|
||||
if (! WebPAnimEncoderAssemble (enc, &webp_data))
|
||||
{
|
||||
g_printerr ("ERROR: %s\n",
|
||||
WebPAnimEncoderGetError (enc));
|
||||
status = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Create a mux object if profile is present */
|
||||
if (profile)
|
||||
{
|
||||
WebPMux *mux;
|
||||
WebPData chunk;
|
||||
const guint8 *icc_data;
|
||||
gsize icc_data_size;
|
||||
|
||||
mux = WebPMuxCreate (&webp_data, 1);
|
||||
if (mux == NULL)
|
||||
{
|
||||
g_printerr ("ERROR: could not extract muxing object\n");
|
||||
status = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Save ICC data */
|
||||
icc_data = pika_color_profile_get_icc_profile (profile, &icc_data_size);
|
||||
chunk.bytes = icc_data;
|
||||
chunk.size = icc_data_size;
|
||||
WebPMuxSetChunk (mux, "ICCP", &chunk, 1);
|
||||
|
||||
WebPDataClear (&webp_data);
|
||||
if (WebPMuxAssemble (mux, &webp_data) != WEBP_MUX_OK)
|
||||
{
|
||||
g_printerr ("ERROR: could not assemble final bytestream\n");
|
||||
status = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
webp_anim_file_writer (outfile, webp_data.bytes, webp_data.size);
|
||||
}
|
||||
while (0);
|
||||
|
||||
/* Free any resources */
|
||||
WebPDataClear (&webp_data);
|
||||
WebPAnimEncoderDelete (enc);
|
||||
g_clear_object (&profile);
|
||||
|
||||
if (prev_frame != NULL)
|
||||
{
|
||||
g_object_unref (prev_frame);
|
||||
}
|
||||
|
||||
if (outfile)
|
||||
fclose (outfile);
|
||||
|
||||
g_list_free (layers);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
webp_decide_output (PikaImage *image,
|
||||
GObject *config,
|
||||
PikaColorProfile **profile,
|
||||
gboolean *out_linear)
|
||||
{
|
||||
gboolean save_profile;
|
||||
|
||||
g_return_if_fail (profile && *profile == NULL);
|
||||
|
||||
g_object_get (config,
|
||||
"save-color-profile", &save_profile,
|
||||
NULL);
|
||||
|
||||
*out_linear = FALSE;
|
||||
|
||||
if (save_profile)
|
||||
{
|
||||
*profile = pika_image_get_color_profile (image);
|
||||
|
||||
/* If a profile is explicitly set, follow its TRC, whatever the
|
||||
* storage format.
|
||||
*/
|
||||
if (*profile && pika_color_profile_is_linear (*profile))
|
||||
*out_linear = TRUE;
|
||||
|
||||
/* When no profile was explicitly set, since WebP is apparently
|
||||
* 8-bit max, we export it as sRGB to avoid shadow posterization
|
||||
* (we don't care about storage TRC).
|
||||
* We do an exception for 8-bit linear work image to avoid
|
||||
* conversion loss while the precision is the same.
|
||||
*/
|
||||
if (! *profile)
|
||||
{
|
||||
/* There is always an effective profile. */
|
||||
*profile = pika_image_get_effective_color_profile (image);
|
||||
|
||||
if (pika_color_profile_is_linear (*profile))
|
||||
{
|
||||
if (pika_image_get_precision (image) != PIKA_PRECISION_U8_LINEAR)
|
||||
{
|
||||
/* If stored data was linear, let's convert the profile. */
|
||||
PikaColorProfile *saved_profile;
|
||||
|
||||
saved_profile = pika_color_profile_new_srgb_trc_from_color_profile (*profile);
|
||||
g_clear_object (profile);
|
||||
*profile = saved_profile;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Keep linear profile as-is for 8-bit linear image. */
|
||||
*out_linear = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
44
plug-ins/file-webp/file-webp-save.h
Normal file
44
plug-ins/file-webp/file-webp-save.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* file-webp - WebP file format plug-in for the PIKA
|
||||
* Copyright (C) 2015 Nathan Osman
|
||||
* Copyright (C) 2016 Ben Touchette
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __WEBP_SAVE_H__
|
||||
#define __WEBP_SAVE_H__
|
||||
|
||||
|
||||
gboolean save_layer (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GObject *config,
|
||||
GError **error);
|
||||
|
||||
gboolean save_animation (GFile *file,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GObject *config,
|
||||
GError **error);
|
||||
|
||||
|
||||
#endif /* __WEBP_SAVE_H__ */
|
392
plug-ins/file-webp/file-webp.c
Normal file
392
plug-ins/file-webp/file-webp.c
Normal file
@ -0,0 +1,392 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* file-webp - WebP file format plug-in for the PIKA
|
||||
* Copyright (C) 2015 Nathan Osman
|
||||
* Copyright (C) 2016 Ben Touchette
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include <webp/encode.h>
|
||||
|
||||
#include "file-webp-dialog.h"
|
||||
#include "file-webp-load.h"
|
||||
#include "file-webp-save.h"
|
||||
#include "file-webp.h"
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
typedef struct _Webp Webp;
|
||||
typedef struct _WebpClass WebpClass;
|
||||
|
||||
struct _Webp
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _WebpClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#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 (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * webp_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * webp_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static PikaValueArray * webp_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Webp, webp, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (WEBP_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
webp_class_init (WebpClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = webp_query_procedures;
|
||||
plug_in_class->create_procedure = webp_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
webp_init (Webp *webp)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
webp_query_procedures (PikaPlugIn *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 PikaProcedure *
|
||||
webp_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
webp_load, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("WebP image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Loads images in the WebP file format",
|
||||
"Loads images in the WebP file format",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Nathan Osman, Ben Touchette",
|
||||
"(C) 2015-2016 Nathan Osman, "
|
||||
"(C) 2016 Ben Touchette",
|
||||
"2015,2016");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/webp");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"webp");
|
||||
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
|
||||
"8,string,WEBP");
|
||||
}
|
||||
else if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
webp_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("WebP image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Saves files in the WebP image format",
|
||||
"Saves files in the WebP image format",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Nathan Osman, Ben Touchette",
|
||||
"(C) 2015-2016 Nathan Osman, "
|
||||
"(C) 2016 Ben Touchette",
|
||||
"2015,2016");
|
||||
|
||||
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
|
||||
_("WebP"));
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/webp");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"webp");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "preset",
|
||||
_("Source _type"),
|
||||
_("WebP encoder preset (Default=0, Picture=1, Photo=2, Drawing=3, "
|
||||
"Icon=4, Text=5)"),
|
||||
0, 5, WEBP_PRESET_DEFAULT,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "lossless",
|
||||
_("L_ossless"),
|
||||
_("Use lossless encoding"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "quality",
|
||||
_("Image _quality"),
|
||||
_("Quality of the image"),
|
||||
0, 100, 90,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "alpha-quality",
|
||||
_("Alpha q_uality"),
|
||||
_("Quality of the image's alpha channel"),
|
||||
0, 100, 100,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "use-sharp-yuv",
|
||||
_("Use Sharp YU_V"),
|
||||
/* TRANSLATORS: \xe2\x86\x92 is a Unicode
|
||||
* "Rightward Arrow" in UTF-8 encoding.
|
||||
*/
|
||||
_("Use sharper (but slower) RGB\xe2\x86\x92YUV conversion"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "animation-loop",
|
||||
_("Loop _forever"),
|
||||
_("Loop animation infinitely"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "minimize-size",
|
||||
_("_Minimize output size (slower)"),
|
||||
_("Minimize output file size"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "keyframe-distance",
|
||||
_("Max distance between _key-frames"),
|
||||
_("Maximum distance between keyframes"),
|
||||
0, G_MAXINT, 50,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "default-delay",
|
||||
_("_Default delay between frames"),
|
||||
_("Default delay (in milliseconds) to use when timestamps"
|
||||
" for frames are not available or forced."),
|
||||
0, G_MAXINT, 200,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "force-delay",
|
||||
_("Use default dela_y for all frames"),
|
||||
_("Force default delay on all frames"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "animation",
|
||||
_("Save a_nimation"),
|
||||
_("Use layers for animation"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
pika_save_procedure_set_support_exif (PIKA_SAVE_PROCEDURE (procedure), TRUE);
|
||||
pika_save_procedure_set_support_iptc (PIKA_SAVE_PROCEDURE (procedure), TRUE);
|
||||
pika_save_procedure_set_support_xmp (PIKA_SAVE_PROCEDURE (procedure), TRUE);
|
||||
pika_save_procedure_set_support_profile (PIKA_SAVE_PROCEDURE (procedure), TRUE);
|
||||
pika_save_procedure_set_support_thumbnail (PIKA_SAVE_PROCEDURE (procedure), TRUE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
webp_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
image = load_image (file, FALSE, &error);
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
webp_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
PikaMetadata *metadata;
|
||||
gboolean animation;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
metadata = pika_procedure_config_begin_export (config, image, run_mode,
|
||||
args, "image/webp");
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE ||
|
||||
run_mode == PIKA_RUN_WITH_LAST_VALS)
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
{
|
||||
if (! save_dialog (image, procedure, G_OBJECT (config)))
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
g_object_get (config,
|
||||
"animation", &animation,
|
||||
NULL);
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE ||
|
||||
run_mode == PIKA_RUN_WITH_LAST_VALS)
|
||||
{
|
||||
PikaExportCapabilities capabilities = (PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA);
|
||||
|
||||
if (animation)
|
||||
capabilities |= PIKA_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION;
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "WebP",
|
||||
capabilities);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (animation)
|
||||
{
|
||||
if (! save_animation (file, image, n_drawables, drawables, G_OBJECT (config),
|
||||
&error))
|
||||
{
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("The WebP plug-in cannot export multiple layer, except in animation mode."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
if (! save_layer (file, image, drawables[0], G_OBJECT (config),
|
||||
&error))
|
||||
{
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == PIKA_PDB_SUCCESS && metadata)
|
||||
{
|
||||
gboolean save_xmp;
|
||||
|
||||
/* WebP doesn't support iptc natively and sets it via xmp */
|
||||
g_object_get (config,
|
||||
"save-xmp", &save_xmp,
|
||||
NULL);
|
||||
g_object_set (config,
|
||||
"save-iptc", save_xmp,
|
||||
NULL);
|
||||
|
||||
pika_metadata_set_bits_per_sample (metadata, 8);
|
||||
}
|
||||
|
||||
pika_procedure_config_end_export (config, image, file, status);
|
||||
g_object_unref (config);
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
36
plug-ins/file-webp/file-webp.h
Normal file
36
plug-ins/file-webp/file-webp.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* file-webp - WebP file format plug-in for the PIKA
|
||||
* Copyright (C) 2015 Nathan Osman
|
||||
* Copyright (C) 2016 Ben Touchette
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FILE_WEBP_H__
|
||||
#define __FILE_WEBP_H__
|
||||
|
||||
|
||||
#define LOAD_PROC "file-webp-load"
|
||||
#define SAVE_PROC "file-webp-save"
|
||||
#define PLUG_IN_BINARY "file-webp"
|
||||
#define PLUG_IN_ROLE "pika-file-webp"
|
||||
|
||||
|
||||
#endif /* __FILE_WEBP_H__ */
|
37
plug-ins/file-webp/meson.build
Normal file
37
plug-ins/file-webp/meson.build
Normal file
@ -0,0 +1,37 @@
|
||||
if webp_found
|
||||
|
||||
plugin_name = 'file-webp'
|
||||
|
||||
plugin_sources = [
|
||||
'file-webp-dialog.c',
|
||||
'file-webp-load.c',
|
||||
'file-webp-save.c',
|
||||
'file-webp.c',
|
||||
]
|
||||
|
||||
if platform_windows
|
||||
plugin_sources += windows.compile_resources(
|
||||
pika_plugins_rc,
|
||||
args: [
|
||||
'--define', 'ORIGINALFILENAME_STR="@0@"'.format(plugin_name+'.exe'),
|
||||
'--define', 'INTERNALNAME_STR="@0@"' .format(plugin_name),
|
||||
'--define', 'TOP_SRCDIR="@0@"' .format(meson.project_source_root()),
|
||||
],
|
||||
include_directories: [
|
||||
rootInclude, appInclude,
|
||||
],
|
||||
)
|
||||
endif
|
||||
|
||||
executable(plugin_name,
|
||||
plugin_sources,
|
||||
dependencies: [
|
||||
libpikaui_dep,
|
||||
gexiv2,
|
||||
webp_libs,
|
||||
],
|
||||
install: true,
|
||||
install_dir: pikaplugindir / 'plug-ins' / plugin_name,
|
||||
)
|
||||
|
||||
endif
|
Reference in New Issue
Block a user