[gimp] Bug 769651 - Add webp file format support to gimp master



commit c1021a3e3a8d4574505c8008ea619018a8fb1b69
Author: Benoit Touchette <draekko software+gimp gmail com>
Date:   Mon Aug 8 16:39:30 2016 -0400

    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.

 configure.ac                          |   37 ++
 plug-ins/Makefile.am                  |    5 +
 plug-ins/file-webp/.gitignore         |    7 +
 plug-ins/file-webp/Makefile.am        |   64 ++++
 plug-ins/file-webp/file-webp-dialog.c |  287 +++++++++++++++
 plug-ins/file-webp/file-webp-dialog.h |   34 ++
 plug-ins/file-webp/file-webp-load.c   |  275 +++++++++++++++
 plug-ins/file-webp/file-webp-load.h   |   31 ++
 plug-ins/file-webp/file-webp-save.c   |  619 +++++++++++++++++++++++++++++++++
 plug-ins/file-webp/file-webp-save.h   |   46 +++
 plug-ins/file-webp/file-webp.c        |  270 ++++++++++++++
 plug-ins/file-webp/file-webp.h        |   28 ++
 po-plug-ins/POTFILES.in               |    4 +
 13 files changed, 1707 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 3d9ba74..e12baa0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am
index 8ba9a45..d60046c 100644
--- a/plug-ins/Makefile.am
+++ b/plug-ins/Makefile.am
@@ -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                    \
diff --git a/plug-ins/file-webp/.gitignore b/plug-ins/file-webp/.gitignore
new file mode 100644
index 0000000..380c017
--- /dev/null
+++ b/plug-ins/file-webp/.gitignore
@@ -0,0 +1,7 @@
+/Makefile.in
+/Makefile
+/.deps
+/_libs
+/.libs
+/file-webp
+/file-webp.exe
diff --git a/plug-ins/file-webp/Makefile.am b/plug-ins/file-webp/Makefile.am
new file mode 100644
index 0000000..296c904
--- /dev/null
+++ b/plug-ins/file-webp/Makefile.am
@@ -0,0 +1,64 @@
+## 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)
diff --git a/plug-ins/file-webp/file-webp-dialog.c b/plug-ins/file-webp/file-webp-dialog.c
new file mode 100644
index 0000000..96150a1
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-dialog.c
@@ -0,0 +1,287 @@
+/* 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 <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "file-webp.h"
+#include "file-webp-dialog.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+void          save_dialog_response      (GtkWidget *widget,
+                                         gint       response_id,
+                                         gpointer   data);
+GtkListStore * save_dialog_presets      (void);
+void           save_dialog_set_preset   (GtkWidget *widget,
+                                         gpointer   data);
+void           save_dialog_toggle_scale (GtkWidget *widget,
+                                         gpointer   data);
+
+
+struct
+{
+  const gchar *id;
+  const gchar *label;
+} presets[] =
+{
+  { "default", "Default" },
+  { "picture", "Picture" },
+  { "photo",   "Photo" },
+  { "drawing", "Drawing" },
+  { "icon",    "Icon" },
+  { "text",    "Text" },
+  { 0 }
+};
+
+
+void
+save_dialog_response (GtkWidget *widget,
+                      gint       response_id,
+                      gpointer   data)
+{
+  /* Store the response */
+  *(GtkResponseType *)data = response_id;
+
+  /* Close the dialog */
+  gtk_widget_destroy (widget);
+}
+
+GtkListStore *
+save_dialog_presets (void)
+{
+  GtkListStore *list_store;
+  int           i;
+
+  /* Create the model */
+  list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+  /* Insert the entries */
+  for (i = 0; presets[i].id; ++i)
+    {
+      gtk_list_store_insert_with_values (list_store,
+                                         NULL,
+                                         -1,
+                                         0, presets[i].id,
+                                         1, presets[i].label,
+                                         -1);
+    }
+
+  return list_store;
+}
+
+void
+save_dialog_set_preset (GtkWidget *widget,
+                        gpointer   data)
+{
+  *(gchar **) data =
+    gimp_string_combo_box_get_active (GIMP_STRING_COMBO_BOX (widget));
+}
+
+void
+save_dialog_toggle_scale (GtkWidget *widget,
+                          gpointer   data)
+{
+  gimp_scale_entry_set_sensitive (GTK_OBJECT (data),
+                                  ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
+}
+
+GtkResponseType
+save_dialog (WebPSaveParams *params,
+             gint32          image_ID,
+             gint32          nLayers)
+{
+  GtkWidget       *dialog;
+  GtkWidget       *vbox;
+  GtkWidget       *label;
+  GtkWidget       *table;
+  GtkWidget       *preset_label;
+  GtkListStore    *preset_list;
+  GtkWidget       *preset_combo;
+  GtkWidget       *lossless_checkbox;
+  GtkWidget       *animation_checkbox;
+  GtkWidget       *loop_anim_checkbox;
+  GtkObject       *quality_scale;
+  GtkObject       *alpha_quality_scale;
+  GtkResponseType  response;
+  gboolean         animation_supported = FALSE;
+  int              slider1 , slider2;
+
+  animation_supported = nLayers > 1;
+
+  /* Create the dialog */
+  dialog = gimp_export_dialog_new (_("WebP"),BINARY_NAME,
+                                   SAVE_PROCEDURE);
+
+  /* Store the response when the dialog is closed */
+  g_signal_connect (dialog, "response",
+                    G_CALLBACK (save_dialog_response),
+                    &response);
+
+  /* Quit the main loop when the dialog is closed */
+  g_signal_connect (dialog, "destroy",
+                    G_CALLBACK (gtk_main_quit),
+                    NULL);
+
+  /* Create the vbox */
+  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+  gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+                      vbox, FALSE, FALSE, 0);
+  gtk_widget_show (vbox);
+
+  /* Create the descriptive label at the top */
+  label = gtk_label_new (_("Use the options below to customize the image."));
+  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+  gtk_widget_show (label);
+
+  /* Create the table */
+  table = gtk_table_new (4, 5, FALSE);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+  gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+  gtk_widget_show (table);
+
+  /* Create the label for the selecting a preset */
+  preset_label = gtk_label_new (_("Preset:"));
+  gtk_table_attach (GTK_TABLE (table), preset_label,
+                    0, 1, 0, 1,
+                    0, 0, 0, 0);
+  gtk_widget_show (preset_label);
+
+  /* Create the combobox containing the presets */
+  preset_list = save_dialog_presets ();
+  preset_combo = gimp_string_combo_box_new (GTK_TREE_MODEL (preset_list), 0, 1);
+  g_object_unref (preset_list);
+
+  gimp_string_combo_box_set_active (GIMP_STRING_COMBO_BOX (preset_combo),
+                                    params->preset);
+  gtk_table_attach (GTK_TABLE (table), preset_combo,
+                    1, 3, 0, 1,
+                    GTK_FILL, GTK_FILL, 0, 0);
+  gtk_widget_show (preset_combo);
+
+  g_signal_connect (preset_combo, "changed",
+                    G_CALLBACK (save_dialog_set_preset),
+                    &params->preset);
+
+  /* Create the lossless checkbox */
+  lossless_checkbox = gtk_check_button_new_with_label (_("Lossless"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lossless_checkbox),
+                                params->lossless);
+  gtk_table_attach (GTK_TABLE (table), lossless_checkbox,
+                    1, 3, 1, 2,
+                   GTK_FILL, GTK_FILL, 0, 0);
+  gtk_widget_show (lossless_checkbox);
+
+  g_signal_connect (lossless_checkbox, "toggled",
+                    G_CALLBACK (gimp_toggle_button_update),
+                    &params->lossless);
+
+  slider1 = 2;
+  slider2 = 3;
+  if (animation_supported)
+    {
+      slider1 = 4;
+      slider2 = 5;
+
+      /* Create the animation checkbox */
+      animation_checkbox = gtk_check_button_new_with_label (_("Use animation"));
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (animation_checkbox),
+                                    params->animation);
+      gtk_table_attach (GTK_TABLE (table), animation_checkbox,
+                        1, 3, 2, 3,
+                        GTK_FILL, GTK_FILL, 0, 0);
+      gtk_widget_show (animation_checkbox);
+
+      g_signal_connect (animation_checkbox, "toggled",
+                        G_CALLBACK (gimp_toggle_button_update),
+                        &params->animation);
+
+      /* Create the loop animation checkbox */
+      loop_anim_checkbox = gtk_check_button_new_with_label (_("Loop infinitely"));
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (loop_anim_checkbox),
+                                    params->loop);
+      gtk_table_attach (GTK_TABLE (table), loop_anim_checkbox,
+                        1, 3, 3, 4,
+                        GTK_FILL, GTK_FILL, 0, 0);
+      gtk_widget_show (loop_anim_checkbox);
+
+      g_signal_connect (loop_anim_checkbox, "toggled",
+                        G_CALLBACK (gimp_toggle_button_update),
+                        &params->loop);
+    }
+
+  /* Create the slider for image quality */
+  quality_scale = gimp_scale_entry_new (GTK_TABLE (table),
+                                        0, slider1,
+                                        _("Image quality:"),
+                                        125,
+                                        0,
+                                        params->quality,
+                                        0.0, 100.0,
+                                        1.0, 10.0,
+                                        0, TRUE,
+                                        0.0, 0.0,
+                                        _("Image quality"),
+                                        NULL);
+  gimp_scale_entry_set_sensitive (quality_scale, ! params->lossless);
+
+  g_signal_connect (quality_scale, "value-changed",
+                    G_CALLBACK (gimp_float_adjustment_update),
+                    &params->quality);
+
+  /* Create the slider for alpha channel quality */
+  alpha_quality_scale = gimp_scale_entry_new (GTK_TABLE (table),
+                                              0, slider2,
+                                              _("Alpha quality:"),
+                                              125,
+                                              0,
+                                              params->alpha_quality,
+                                              0.0, 100.0,
+                                              1.0, 10.0,
+                                              0, TRUE,
+                                              0.0, 0.0,
+                                              _("Alpha channel quality"),
+                                              NULL);
+  gimp_scale_entry_set_sensitive (alpha_quality_scale, ! params->lossless);
+
+  g_signal_connect (alpha_quality_scale, "value-changed",
+                    G_CALLBACK (gimp_float_adjustment_update),
+                    &params->alpha_quality);
+
+  /* Enable and disable the sliders when the lossless option is selected */
+  g_signal_connect (lossless_checkbox, "toggled",
+                    G_CALLBACK (save_dialog_toggle_scale),
+                    quality_scale);
+  g_signal_connect (lossless_checkbox, "toggled",
+                    G_CALLBACK (save_dialog_toggle_scale),
+                    alpha_quality_scale);
+
+  /* Display the dialog and enter the main event loop */
+  gtk_widget_show (dialog);
+  gtk_main ();
+
+  return response;
+}
diff --git a/plug-ins/file-webp/file-webp-dialog.h b/plug-ins/file-webp/file-webp-dialog.h
new file mode 100644
index 0000000..e0bec97
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-dialog.h
@@ -0,0 +1,34 @@
+/* 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__ */
diff --git a/plug-ins/file-webp/file-webp-load.c b/plug-ins/file-webp/file-webp-load.c
new file mode 100644
index 0000000..4c40490
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-load.c
@@ -0,0 +1,275 @@
+/* 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;
+}
diff --git a/plug-ins/file-webp/file-webp-load.h b/plug-ins/file-webp/file-webp-load.h
new file mode 100644
index 0000000..2622210
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-load.h
@@ -0,0 +1,31 @@
+/* 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__ */
diff --git a/plug-ins/file-webp/file-webp-save.c b/plug-ins/file-webp/file-webp-save.c
new file mode 100644
index 0000000..efc0b1f
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-save.c
@@ -0,0 +1,619 @@
+/* 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 <errno.h>
+#include <glib/gstdio.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <gegl.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <webp/encode.h>
+#include <webp/mux.h>
+
+#include "file-webp-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+WebPPreset    webp_preset_by_name   (gchar             *name);
+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);
+const gchar * webp_error_string     (WebPEncodingError  error_code);
+
+gboolean      save_layer            (const gchar       *filename,
+                                     gint32             nLayers,
+                                     gint32             image_ID,
+                                     gint32             drawable_ID,
+                                     WebPSaveParams    *params,
+                                     GError           **error);
+
+gboolean      save_animation        (const gchar       *filename,
+                                     gint32             nLayers,
+                                     gint32            *allLayers,
+                                     gint32             image_ID,
+                                     gint32             drawable_ID,
+                                     WebPSaveParams    *params,
+                                     GError           **error);
+
+
+WebPPreset
+webp_preset_by_name (gchar *name)
+{
+  if( ! strcmp (name, "picture"))
+    {
+      return WEBP_PRESET_PICTURE;
+    }
+  else if (! strcmp (name, "photo"))
+    {
+      return WEBP_PRESET_PHOTO;
+    }
+  else if (! strcmp (name, "drawing"))
+    {
+      return WEBP_PRESET_DRAWING;
+    }
+  else if (! strcmp (name, "icon"))
+    {
+      return WEBP_PRESET_ICON;
+    }
+  else if (! strcmp (name, "text"))
+    {
+      return WEBP_PRESET_TEXT;
+    }
+  else
+    {
+      return WEBP_PRESET_DEFAULT;
+    }
+}
+
+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 gimp_progress_update (percent / 100.0);
+}
+
+const gchar *
+webp_error_string (WebPEncodingError error_code)
+{
+  switch (error_code)
+    {
+    case VP8_ENC_ERROR_OUT_OF_MEMORY:
+      return _("out of memory");
+    case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
+      return _("not enough memory to flush bits");
+    case VP8_ENC_ERROR_NULL_PARAMETER:
+      return _("NULL parameter");
+    case VP8_ENC_ERROR_INVALID_CONFIGURATION:
+      return _("invalid configuration");
+    case VP8_ENC_ERROR_BAD_DIMENSION:
+      return _("bad image dimensions");
+    case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
+      return _("partition is bigger than 512K");
+    case VP8_ENC_ERROR_PARTITION_OVERFLOW:
+      return _("partition is bigger than 16M");
+    case VP8_ENC_ERROR_BAD_WRITE:
+      return _("unable to flush bytes");
+    case VP8_ENC_ERROR_FILE_TOO_BIG:
+      return _("file is larger than 4GiB");
+    case VP8_ENC_ERROR_USER_ABORT:
+      return _("user aborted encoding");
+    case VP8_ENC_ERROR_LAST:
+      return _("list terminator");
+    default:
+      return _("unknown error");
+    }
+}
+
+gboolean
+save_layer (const gchar    *filename,
+            gint32          nLayers,
+            gint32          image_ID,
+            gint32          drawable_ID,
+            WebPSaveParams *params,
+            GError        **error)
+{
+  gboolean          status   = FALSE;
+  FILE             *outfile  = NULL;
+  WebPConfig        config   = {0};
+  WebPPicture       picture  = {0};
+  guchar           *buffer   = NULL;
+  gint              w, h;
+  gint              bpp;
+  GimpColorProfile *profile;
+  GimpImageType     drawable_type;
+  GeglBuffer       *geglbuffer = NULL;
+  GeglRectangle     extent;
+  gchar            *indata;
+  gsize             indatalen;
+  struct            stat stsz;
+  int               fd_outfile;
+  WebPData          chunk;
+  int               res;
+
+  /* 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 */
+      gimp_progress_init_printf (_("Saving '%s'"),
+                                 gimp_filename_to_utf8(filename));
+
+      /* Attempt to open the output file */
+      if ((outfile = g_fopen (filename, "wb+")) == NULL)
+        {
+          g_set_error (error, G_FILE_ERROR,
+                       g_file_error_from_errno (errno),
+                       _("Unable to open '%s' for writing"),
+                       gimp_filename_to_utf8 (filename));
+          break;
+        }
+
+      /* Obtain the drawable type */
+      drawable_type = gimp_drawable_type (drawable_ID);
+
+      /* Retrieve the buffer for the layer */
+      geglbuffer = gimp_drawable_get_buffer (drawable_ID);
+      extent = *gegl_buffer_get_extent (geglbuffer);
+      bpp = gimp_drawable_bpp (drawable_ID);
+      w = extent.width;
+      h = extent.height;
+
+      /* Initialize the WebP configuration with a preset and fill in the
+       * remaining values */
+      WebPConfigPreset (&config,
+                        webp_preset_by_name (params->preset),
+                        params->quality);
+
+      config.lossless      = params->lossless;
+      config.method        = 6;  /* better quality */
+      config.alpha_quality = params->alpha_quality;
+
+      /* 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 = (guchar *) g_malloc (w * h * bpp);
+      if(! buffer)
+        break;
+
+      /* Read the region into the buffer */
+      gegl_buffer_get (geglbuffer, &extent, 1.0, NULL, buffer,
+                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+      /* Use the appropriate function to import the data from the buffer */
+      if (drawable_type == GIMP_RGB_IMAGE)
+        {
+          WebPPictureImportRGB (&picture, buffer, w * bpp);
+        }
+      else
+        {
+          WebPPictureImportRGBA (&picture, buffer, w * bpp);
+        }
+
+      /* Perform the actual encode */
+      if (! WebPEncode (&config, &picture))
+        {
+          g_printerr ("WebP error: '%s'",
+                      webp_error_string (picture.error_code));
+          g_set_error (error, G_FILE_ERROR,
+                       picture.error_code,
+                       _("WebP error: '%s'"),
+                       webp_error_string (picture.error_code));
+          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)
+    {
+      gegl_buffer_flush (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)
+            {
+              gboolean saved = FALSE;
+
+              /* Save ICC data */
+              profile = gimp_image_get_color_profile (image_ID);
+              if (profile)
+                {
+                  const guint8 *icc_data;
+                  gsize         icc_data_size;
+
+                  saved = TRUE;
+
+                  icc_data = gimp_color_profile_get_icc_profile (profile,
+                                                                 &icc_data_size);
+                  chunk.bytes = icc_data;
+                  chunk.size = icc_data_size;
+                  WebPMuxSetChunk(mux, "ICCP", &chunk, 1);
+                  g_object_unref (profile);
+                }
+
+              if (saved == TRUE)
+                {
+                  WebPMuxAssemble (mux, &wp_data);
+                  rewind (outfile);
+                  webp_anim_file_writer (outfile, wp_data.bytes, wp_data.size);
+                }
+            }
+          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);
+
+  if (buffer)
+    free (buffer);
+
+  WebPPictureFree (&picture);
+
+  return status;
+}
+
+gboolean
+save_animation (const gchar    *filename,
+                gint32          nLayers,
+                gint32         *allLayers,
+                gint32          image_ID,
+                gint32          drawable_ID,
+                WebPSaveParams *params,
+                GError        **error)
+{
+  gboolean               status   = FALSE;
+  FILE                  *outfile  = NULL;
+  guchar                *buffer   = NULL;
+  gint                   w, h, bpp;
+  GimpImageType          drawable_type;
+  GimpColorProfile      *profile;
+  WebPAnimEncoderOptions enc_options;
+  WebPData               webp_data;
+  int                    frame_timestamp = 0;
+  WebPAnimEncoder       *enc;
+  WebPMux               *mux;
+  WebPMuxAnimParams      anim_params = {0};
+
+  if (nLayers < 1)
+    return FALSE;
+
+  do
+    {
+      gint loop;
+
+      /* Begin displaying export progress */
+      gimp_progress_init_printf (_("Saving '%s'"),
+                                 gimp_filename_to_utf8 (filename));
+
+      /* Attempt to open the output file */
+      if ((outfile = g_fopen (filename, "wb")) == NULL)
+        {
+          g_set_error (error, G_FILE_ERROR,
+                       g_file_error_from_errno (errno),
+                       _("Unable to open '%s' for writing"),
+                       gimp_filename_to_utf8 (filename));
+          break;
+        }
+
+      WebPDataInit (&webp_data);
+      if (! WebPAnimEncoderOptionsInit (&enc_options))
+        {
+          g_printerr ("ERROR: verion mismatch\n");
+          break;
+        }
+
+      for (loop = 0; loop < nLayers; loop++)
+        {
+          GeglBuffer       *geglbuffer;
+          GeglRectangle     extent;
+          WebPConfig        config;
+          WebPPicture       picture;
+          WebPMemoryWriter  mw = { 0 };
+
+          /* Obtain the drawable type */
+          drawable_type = gimp_drawable_type (allLayers[loop]);
+
+          /* Retrieve the buffer for the layer */
+          geglbuffer = gimp_drawable_get_buffer (allLayers[loop]);
+          extent = *gegl_buffer_get_extent (geglbuffer);
+          bpp = gimp_drawable_bpp (allLayers[loop]);
+          w = extent.width;
+          h = extent.height;
+
+          if (loop == 0)
+            {
+              enc = WebPAnimEncoderNew (w, h, &enc_options);
+              if (! enc)
+                {
+                  g_printerr ("ERROR: enc == null\n");
+                  break;
+                }
+            }
+
+          /* Attempt to allocate a buffer of the appropriate size */
+          buffer = (guchar *) g_malloc (w * h * bpp);
+          if(! buffer)
+            {
+              g_printerr ("Buffer error: 'buffer null'\n");
+              status = FALSE;
+              break;
+            }
+
+          WebPConfigInit (&config);
+          WebPConfigPreset (&config,
+                            webp_preset_by_name (params->preset),
+                            params->quality);
+
+          config.lossless      = params->lossless;
+          config.method        = 6;  /* better quality */
+          config.alpha_quality = params->alpha_quality;
+          config.exact         = 1;
+
+          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;
+          picture.progress_hook = webp_file_progress;
+
+          /* Read the region into the buffer */
+          gegl_buffer_get (geglbuffer, &extent, 1.0, NULL, buffer,
+                           GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+          /* Use the appropriate function to import the data from the buffer */
+          if (drawable_type == GIMP_RGB_IMAGE)
+            {
+              WebPPictureImportRGB (&picture, buffer, w * bpp);
+            }
+          else
+            {
+              WebPPictureImportRGBA (&picture, buffer, w * bpp);
+            }
+
+          /* Perform the actual encode */
+          if (! WebPAnimEncoderAdd (enc, &picture, frame_timestamp, &config))
+            {
+              g_printerr ("ERROR[%d]: %s\n",
+                          picture.error_code,
+                          webp_error_string (picture.error_code));
+            }
+
+          WebPMemoryWriterClear (&mw);
+          WebPPictureFree (&picture);
+
+          if (buffer)
+            free (buffer);
+
+          /* Flush the drawable and detach */
+          gegl_buffer_flush (geglbuffer);
+          g_object_unref (geglbuffer);
+        }
+
+      WebPAnimEncoderAdd (enc, NULL, frame_timestamp, NULL);
+
+      if (! WebPAnimEncoderAssemble (enc, &webp_data))
+        {
+          g_printerr ("ERROR: %s\n",
+                      WebPAnimEncoderGetError (enc));
+        }
+
+      /* Set animations parameters */
+      mux = WebPMuxCreate (&webp_data, 1);
+
+      anim_params.loop_count = 0;
+      if (params->loop == FALSE)
+        {
+          anim_params.loop_count = 1;
+        }
+
+      WebPMuxSetAnimationParams (mux, &anim_params);
+
+      /* Save ICC data */
+      profile = gimp_image_get_color_profile (image_ID);
+      if (profile)
+        {
+          WebPData      chunk;
+          const guint8 *icc_data;
+          gsize         icc_data_size;
+
+          icc_data = gimp_color_profile_get_icc_profile (profile, &icc_data_size);
+          chunk.bytes = icc_data;
+          chunk.size  = icc_data_size;
+          WebPMuxSetChunk (mux, "ICCP", &chunk, 1);
+          g_object_unref (profile);
+        }
+
+      WebPMuxAssemble (mux, &webp_data);
+
+      webp_anim_file_writer (outfile, webp_data.bytes, webp_data.size);
+
+      WebPDataClear (&webp_data);
+      WebPAnimEncoderDelete (enc);
+
+      status = TRUE;
+    }
+  while (0);
+
+  /* Free any resources */
+  if (outfile)
+    fclose (outfile);
+
+  return status;
+}
+
+
+gboolean
+save_image (const gchar    *filename,
+            gint32          nLayers,
+            gint32         *allLayers,
+            gint32          image_ID,
+            gint32          drawable_ID,
+            WebPSaveParams *params,
+            GError        **error)
+{
+  GimpMetadata          *metadata;
+  GimpMetadataSaveFlags  metadata_flags;
+  gboolean               status = FALSE;
+  GFile                 *file;
+
+  if (nLayers == 0)
+    return FALSE;
+
+  gegl_init (NULL, NULL);
+
+  g_printerr("Saving WebP file %s\n", filename);
+
+  if (nLayers == 1)
+    {
+      status = save_layer (filename, nLayers, image_ID, drawable_ID, params,
+                           error);
+    }
+  else
+    {
+      if (! params->animation)
+        {
+          status = save_layer (filename,
+                               nLayers, image_ID, drawable_ID, params,
+                               error);
+        }
+      else
+        {
+          status = save_animation (filename,
+                                   nLayers, allLayers, image_ID, drawable_ID,
+                                   params, error);
+        }
+    }
+
+  metadata = gimp_image_metadata_save_prepare (image_ID,
+                                               "image/webp",
+                                               &metadata_flags);
+
+  if (metadata)
+    {
+      gimp_metadata_set_bits_per_sample (metadata, 8);
+      file = g_file_new_for_path (filename);
+      gimp_image_metadata_save_finish (image_ID,
+                                       "image/webp",
+                                       metadata, metadata_flags,
+                                       file, NULL);
+      g_object_unref (file);
+    }
+
+  /* Return the status */
+  return status;
+}
diff --git a/plug-ins/file-webp/file-webp-save.h b/plug-ins/file-webp/file-webp-save.h
new file mode 100644
index 0000000..23c55e6
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-save.h
@@ -0,0 +1,46 @@
+/* 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__ */
diff --git a/plug-ins/file-webp/file-webp.c b/plug-ins/file-webp/file-webp.c
new file mode 100644
index 0000000..451e80d
--- /dev/null
+++ b/plug-ins/file-webp/file-webp.c
@@ -0,0 +1,270 @@
+/* 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;
+
+      /* Load the image and drawable IDs */
+      image_ID    = param[1].data.d_int32;
+      drawable_ID = param[2].data.d_int32;
+
+      layers = gimp_image_get_layers (image_ID, &n_layers);
+
+      /* What happens next depends on the run mode */
+      switch (run_mode)
+        {
+        case GIMP_RUN_INTERACTIVE:
+        case GIMP_RUN_WITH_LAST_VALS:
+
+          gimp_ui_init (BINARY_NAME, FALSE);
+
+          /* Attempt to export the image */
+          export_ret = gimp_export_image (&image_ID,
+                                          &drawable_ID,
+                                          "WEBP",
+                                          GIMP_EXPORT_CAN_HANDLE_RGB |
+                                          GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+          /* Return immediately if canceled */
+          if (export_ret == GIMP_EXPORT_CANCEL)
+            {
+              values[0].data.d_status = GIMP_PDB_CANCEL;
+              return;
+            }
+
+          /* Display the dialog */
+          if (save_dialog (&params, image_ID, n_layers) != GTK_RESPONSE_OK)
+            {
+              values[0].data.d_status = GIMP_PDB_CANCEL;
+              return;
+            }
+
+          break;
+
+        case GIMP_RUN_NONINTERACTIVE:
+
+          /* Ensure the correct number of parameters were supplied */
+          if (nparams != 10)
+            {
+              status = GIMP_PDB_CALLING_ERROR;
+              break;
+            }
+
+          /* Load the parameters */
+          params.preset        = param[5].data.d_string;
+          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;
+
+          break;
+        }
+
+      /* Attempt to save the image */
+      if (! save_image (param[3].data.d_string,
+                        n_layers, layers,
+                        image_ID,
+                        drawable_ID,
+                        &params,
+                        &error))
+        {
+          status = GIMP_PDB_EXECUTION_ERROR;
+        }
+
+      g_free (layers);
+    }
+
+  /* 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;
+}
diff --git a/plug-ins/file-webp/file-webp.h b/plug-ins/file-webp/file-webp.h
new file mode 100644
index 0000000..783aacf
--- /dev/null
+++ b/plug-ins/file-webp/file-webp.h
@@ -0,0 +1,28 @@
+/* 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_H__
+#define __WEBP_H__
+
+extern const char BINARY_NAME[];
+extern const char SAVE_PROCEDURE[];
+
+#endif /* __WEBP_H__ */
diff --git a/po-plug-ins/POTFILES.in b/po-plug-ins/POTFILES.in
index 5de4885..5509882 100644
--- a/po-plug-ins/POTFILES.in
+++ b/po-plug-ins/POTFILES.in
@@ -122,6 +122,10 @@ plug-ins/file-sgi/sgi.c
 plug-ins/file-tiff/file-tiff.c
 plug-ins/file-tiff/file-tiff-load.c
 plug-ins/file-tiff/file-tiff-save.c
+plug-ins/file-webp/file-webp.c
+plug-ins/file-webp/file-webp-dialog.c
+plug-ins/file-webp/file-webp-load.c
+plug-ins/file-webp/file-webp-save.c
 plug-ins/flame/flame.c
 plug-ins/fractal-explorer/fractal-explorer-dialogs.c
 plug-ins/fractal-explorer/fractal-explorer.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]