Commit c1021a3e authored by Benoit Touchette's avatar Benoit Touchette Committed by Michael Natterer

Bug 769651 - Add webp file format support to gimp master

Patch to add webp file format support. Includes supports for
loading/saving, icc profiles, exif, and xmp data.
Co-authored with Nathan Osman.
parent 98e43b94
......@@ -75,6 +75,9 @@ m4_define([openexr_required_version], [1.6.1])
m4_define([gtk_mac_integration_required_version], [2.0.0])
m4_define([intltool_required_version], [0.40.1])
m4_define([python2_required_version], [2.5.0])
m4_define([webp_required_version], [0.5.1])
m4_define([webpmux_required_version], [0.5.1])
m4_define([webpdemux_required_version], [0.5.1])
# Current test considers only 2 version numbers. If we update the recommended
# version of gettext with more version numbers, please update the tests.
......@@ -158,6 +161,9 @@ POPPLER_DATA_REQUIRED_VERSION=poppler_data_required_version
OPENEXR_REQUIRED_VERSION=openexr_required_version
INTLTOOL_REQUIRED_VERSION=intltool_required_version
PYTHON2_REQUIRED_VERSION=python2_required_version
WEBP_REQUIRED_VERSION=webp_required_version
WEBPMUX_REQUIRED_VERSION=webpmux_required_version
WEBPDEMUX_REQUIRED_VERSION=webpdemux_required_version
XGETTEXT_RECOMMENDED_VERSION=xgettext_recommended_version
AC_SUBST(GLIB_REQUIRED_VERSION)
AC_SUBST(GDK_PIXBUF_REQUIRED_VERSION)
......@@ -185,6 +191,9 @@ AC_SUBST(POPPLER_DATA_REQUIRED_VERSION)
AC_SUBST(OPENEXR_REQUIRED_VERSION)
AC_SUBST(INTLTOOL_REQUIRED_VERSION)
AC_SUBST(PYTHON2_REQUIRED_VERSION)
AC_SUBST(WEBP_REQUIRED_VERSION)
AC_SUBST(WEBPMUX_REQUIRED_VERSION)
AC_SUBST(WEBPDEMUX_REQUIRED_VERSION)
AC_SUBST(XGETTEXT_RECOMMENDED_VERSION)
# The symbol GIMP_UNSTABLE is defined above for substitution in
......@@ -1548,6 +1557,32 @@ AC_SUBST(FILE_EXR)
AM_CONDITIONAL(HAVE_OPENEXR, test "x$have_openexr" = xyes)
################
# Check for WebP
################
AC_ARG_WITH(webp, [ --without-webp build without WebP support])
have_webp=no
if test "x$with_webp" != xno; then
have_webp=yes
PKG_CHECK_MODULES(WEBP, libwebp >= webp_required_version,,
[have_webp="no (WebP not found)"])
PKG_CHECK_MODULES(WEBPMUX, libwebpmux >= webpmux_required_version,,
[have_webp="no (WebP not found)"])
PKG_CHECK_MODULES(WEBPDEMUX, libwebpdemux >= webpdemux_required_version,,
[have_webp="no (WebP not found)"])
fi
if test "x$have_webp" = xyes; then
MIME_TYPES="$MIME_TYPES;image/x-webp"
fi
AM_CONDITIONAL(HAVE_WEBP, test "x$have_webp" = xyes)
######################
# Check for libmypaint
######################
......@@ -2400,6 +2435,7 @@ plug-ins/file-jpeg/Makefile
plug-ins/file-psd/Makefile
plug-ins/file-sgi/Makefile
plug-ins/file-tiff/Makefile
plug-ins/file-webp/Makefile
plug-ins/flame/Makefile
plug-ins/fractal-explorer/Makefile
plug-ins/fractal-explorer/examples/Makefile
......@@ -2554,6 +2590,7 @@ Optional Plug-Ins:
JPEG 2000: $have_jp2
MNG: $have_libmng
OpenEXR: $have_openexr
WebP: $have_webp
PDF (import): $have_poppler
PDF (export): $have_cairo_pdf
Print: $enable_print
......
......@@ -25,6 +25,10 @@ if OS_WIN32
twain = twain
endif
if HAVE_WEBP
file_webp = file-webp
endif
SUBDIRS = \
$(script_fu) \
$(pygimp) \
......@@ -39,6 +43,7 @@ SUBDIRS = \
file-psd \
file-sgi \
file-tiff \
$(file_webp) \
flame \
fractal-explorer \
gfig \
......
/Makefile.in
/Makefile
/.deps
/_libs
/.libs
/file-webp
/file-webp.exe
## Process this file with automake to produce Makefile.in
libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
if OS_WIN32
mwindows = -mwindows
endif
if HAVE_WINDRES
include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
file_webp_RC = file-webp.rc.o
endif
AM_LDFLAGS = $(mwindows)
libexecdir = $(gimpplugindir)/plug-ins
AM_CPPFLAGS = \
-I$(top_srcdir) \
$(GTK_CFLAGS) \
$(EXIF_CFLAGS) \
$(GEGL_CFLAGS) \
$(GEXIV2_CFLAGS) \
$(WEBP_CFLAGS) \
$(WEBPMUX_CFLAGS) \
$(WEBPDEMUX_CFLAGS) \
-I$(includedir)
libexec_PROGRAMS = file-webp
file_webp_SOURCES = \
file-webp.c \
file-webp.h \
file-webp-dialog.c \
file-webp-dialog.h \
file-webp-load.c \
file-webp-load.h \
file-webp-save.c \
file-webp-save.h
file_webp_LDADD = \
$(libgimpui) \
$(libgimpwidgets) \
$(libgimpconfig) \
$(libgimp) \
$(libgimpcolor) \
$(libgimpmath) \
$(libgimpbase) \
$(webp_LIBS) \
$(GTK_LIBS) \
$(GEGL_LIBS) \
$(GEXIV2_LIBS) \
$(WEBP_LIBS) \
$(WEBPMUX_LIBS) \
$(WEBPDEMUX_LIBS) \
$(RT_LIBS) \
$(INTLLIBS) \
$(file_webp_RC)
This diff is collapsed.
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* file-webp - WebP file format plug-in for the GIMP
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __WEBP_DIALOG_H__
#define __WEBP_DIALOG_H__
#include "file-webp-save.h"
GtkResponseType save_dialog (WebPSaveParams *params,
gint32 image_ID,
gint32 nLayers);
#endif /* __WEBP_DIALOG_H__ */
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* file-webp - WebP file format plug-in for the GIMP
* 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <gegl.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#include <webp/decode.h>
#include <webp/demux.h>
#include <webp/mux.h>
#include "file-webp-load.h"
#include "libgimp/stdplugins-intl.h"
static void
create_layer (gint32 image_ID,
uint8_t *layer_data,
gint32 position,
gchar *name,
gint width,
gint height,
gint32 offsetx,
gint32 offsety)
{
gint32 layer_ID;
GeglBuffer *geglbuffer;
GeglRectangle extent;
layer_ID = gimp_layer_new (image_ID, name,
width, height,
GIMP_RGBA_IMAGE,
100,
GIMP_NORMAL_MODE);
gimp_image_insert_layer (image_ID, layer_ID, -1, position);
if (offsetx > 0 || offsety > 0)
{
gimp_layer_set_offsets (layer_ID, offsetx, offsety);
}
/* Retrieve the buffer for the layer */
geglbuffer = gimp_drawable_get_buffer (layer_ID);
/* Copy the image data to the region */
gegl_rectangle_set (&extent, 0, 0, width, height);
gegl_buffer_set (geglbuffer, &extent, 0, NULL, layer_data,
GEGL_AUTO_ROWSTRIDE);
/* Flush the drawable and detach */
gegl_buffer_flush (geglbuffer);
if (geglbuffer)
g_object_unref (geglbuffer);
}
gint32
load_image (const gchar *filename,
gboolean interactive,
GError **error)
{
uint8_t *indata = NULL;
uint8_t *outdata;
gsize indatalen;
gint width;
gint height;
gint32 image_ID;
GFile *file;
GimpMetadata *metadata;
WebPMux *mux;
WebPBitstreamFeatures features;
WebPData wp_data;
uint32_t flag;
int animation = FALSE;
int icc = FALSE;
int exif = FALSE;
int xmp = FALSE;
int frames = 0;
gchar *name;
/* Attempt to read the file contents from disk */
if (! g_file_get_contents (filename,
(gchar **) &indata,
&indatalen,
error))
{
return -1;
}
g_printerr ("Loading WebP file %s\n", filename);
/* Validate WebP data */
if (! WebPGetInfo (indata, indatalen, &width, &height))
{
g_printerr ("Invalid WebP file\n");
return -1;
}
gegl_init (NULL, NULL);
wp_data.bytes = indata;
wp_data.size = indatalen;
mux = WebPMuxCreate (&wp_data, 1);
if (! mux)
return -1;
WebPMuxGetFeatures (mux, &flag);
if (flag == 0)
{
animation = FALSE;
icc = FALSE;
exif = FALSE;
xmp = FALSE;
frames = 0;
}
else
{
if (flag & ANIMATION_FLAG)
animation = TRUE;
if (flag & ICCP_FLAG)
icc = TRUE;
if (flag & EXIF_FLAG)
exif = TRUE;
if (flag & 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_ID = gimp_image_new (width, height, GIMP_RGB);
if (! animation)
{
/* Attempt to decode the data as a WebP image */
outdata = WebPDecodeRGBA (indata, indatalen, &width, &height);
/* Free the original compressed data */
g_free (indata);
/* Check to ensure the image data was loaded correctly */
if (! outdata)
return -1;
create_layer (image_ID, outdata, 0, _("Background"),
width, height, 0, 0);
/* Free the image data */
free (outdata);
}
else
{
const WebPChunkId id = WEBP_CHUNK_ANMF;
WebPMuxAnimParams params;
gint loop;
WebPMuxGetAnimationParams (mux, &params);
WebPMuxNumChunks (mux, id, &frames);
/* Attempt to decode the data as a WebP animation image */
for (loop = 0; loop < frames; loop++)
{
WebPMuxFrameInfo thisframe;
gint i = loop;
if (WebPMuxGetFrame (mux, i, &thisframe) == WEBP_MUX_OK)
{
WebPGetFeatures (thisframe.bitstream.bytes,
thisframe.bitstream.size, &features);
outdata = WebPDecodeRGBA (thisframe.bitstream.bytes,
thisframe.bitstream.size,
&width, &height);
if (! outdata)
return -1;
name = g_strdup_printf (_("Frame %d"), loop + 1);
create_layer (image_ID, outdata, 0,
name, width, height,
thisframe.x_offset,
thisframe.y_offset);
g_free (name);
/* Free the image data */
free (outdata);
}
WebPDataClear (&thisframe.bitstream);
}
WebPDataClear (&wp_data);
}
if (icc)
{
WebPData icc_profile;
GimpColorProfile *profile;
WebPMuxGetChunk (mux, "ICCP", &icc_profile);
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);
g_object_unref (profile);
}
}
if (exif || xmp)
{
if (exif)
{
WebPData exif;
WebPMuxGetChunk (mux, "EXIF", &exif);
}
if (xmp)
{
WebPData xmp;
WebPMuxGetChunk (mux, "XMP ", &xmp);
}
file = g_file_new_for_path (filename);
metadata = gimp_image_metadata_load_prepare (image_ID, "image/webp",
file, NULL);
if (metadata)
{
gimp_image_metadata_load_finish (image_ID, "image/webp",
metadata, GIMP_METADATA_LOAD_ALL,
interactive);
g_object_unref (metadata);
}
g_object_unref (file);
}
gimp_image_set_filename (image_ID, filename);
return image_ID;
}
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* file-webp - WebP file format plug-in for the GIMP
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __WEBP_LOAD_H__
#define __WEBP_LOAD_H__
gint32 load_image (const gchar *filename,
gboolean interactive,
GError **error);
#endif /* __WEBP_LOAD_H__ */
This diff is collapsed.
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* file-webp - WebP file format plug-in for the GIMP
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __WEBP_SAVE_H__
#define __WEBP_SAVE_H__
typedef struct
{
gchar *preset;
gboolean lossless;
gboolean animation;
gboolean loop;
gfloat quality;
gfloat alpha_quality;
} WebPSaveParams;
gboolean save_image (const gchar *filename,
gint32 nLayers,
gint32 *allLayers,
gint32 image_ID,
gint32 drawable_ID,
WebPSaveParams *params,
GError **error);
#endif /* __WEBP_SAVE_H__ */
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* file-webp - WebP file format plug-in for the GIMP
* 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.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 "libgimp/stdplugins-intl.h"
const char BINARY_NAME[] = "file-webp";
const char LOAD_PROCEDURE[] = "file-webp-load";
const char SAVE_PROCEDURE[] = "file-webp-save";
/* Predeclare our entrypoints. */
static void query (void);
static void run (const gchar *,
gint,
const GimpParam *,
gint *,
GimpParam **);
/* Declare our plugin entry points. */
GimpPlugInInfo PLUG_IN_INFO =
{
NULL,
NULL,
query,
run
};
MAIN()
/* This function registers our load and save handlers. */
static void
query (void)
{
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" }
};
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_STRING, "preset", "Name of preset to use" },
{ 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_install_procedure (LOAD_PROCEDURE,
"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_PROCEDURE, "image/webp");
gimp_register_load_handler (LOAD_PROCEDURE, "webp", "");
gimp_register_magic_load_handler (LOAD_PROCEDURE,
"webp",
"",
"8,string,WEBP");
gimp_install_procedure (SAVE_PROCEDURE,
"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*",
GIMP_PLUGIN,
G_N_ELEMENTS (save_arguments),
0,
save_arguments,
NULL);
gimp_register_file_handler_mime (SAVE_PROCEDURE, "image/webp");
gimp_register_save_handler (SAVE_PROCEDURE, "webp", "");
}
static void
run (const gchar *name,
gint nparams,
const GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals)
{
static GimpParam values[2];
GimpRunMode run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint32 image_ID;
gint32 drawable_ID;
GError *error = NULL;
/* Determine the current run mode */
run_mode = param[0].data.d_int32;
/* Fill in the return values */
*nreturn_vals = 1;
*return_vals = values;
values[0].type = GIMP_PDB_STATUS;
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
/* Determine which procedure is being invoked */
if (! strcmp (name, LOAD_PROCEDURE))
{
/* No need to determine whether the plugin is being invoked
* interactively here since we don't need a UI for loading
*/
image_ID = load_image (param[1].data.d_string, 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_PROCEDURE))
{
WebPSaveParams params;
GimpExportReturn export_ret = GIMP_EXPORT_CANCEL;
gint32 *layers;
gint32 n_layers;
/* Initialize the parameters to their defaults */
params.preset = "default";
params.lossless = FALSE;
params.animation = FALSE;
params.loop = TRUE;
params.quality = 90.0f;
params.alpha_quality = 100.0f;